一、基本类型
1.c语言整数类型的大小并没有精确的定义,而是随着编译器的类型而变化,ANSI C能保证的只是数据的最小尺寸。char>=8位,short>=16位,int>=16位,long>=32位。
2.因此,可以用typedef定义int16和int32,然后根据实际的机器环境把它们定义为int,short,long等类型。但是,标准头文件
二、指针声明
1.c语言中声明的语法为
基本类型 生成基本类型的东西
基本类型不必多说,生成基本类型的东西包括*p,a[10]或者f()这样的符号,表明被生命的变量是指向基本类型的指针,基本类型的数组或者返回基本类型的函数。所以,
char *p1,p2;//p1为指向char的指针,p1为char变量。
2.如果函数的返回值为void时,则声明和定义时应该书写一致。比如:只有下面两种情况
①声明:f();定义:f(){} ②声明:void f(); 定义:void f(){} (注:切不可交换使用)
三、声明风格
全局变量和函数在某个相关的.c文件中定义,然后在.h中进行外部声明。在其他需要使用的.c文件中,只需要include对应.h文件即可。(注:定义变量的.c文件也应该包含该头文件,便于编译器检查定义和生命的一致性。)
顺便说一下,声明如下:
extern int i;
extern int f();
定义如下:
int i=0;
int f()
{
return 1;
}
三、存储类型
1.static
(有关static的使用方法,可以参考 static使用方法)
简单起见,同一个静态函数或变量的所有声明都必须包含static存储类型。
2.extern
存储类型extern只对数据声明有意义。对于函数,只是一种格式上的提示,说明函数的定义可能在另外一个函数文件中。
3.auto
没有用途,已经过时。它是从c语言的无类型前身B语言中继承下来的。在B语言中,没有像int这样的关键字,声明必须包含存储类型。)
四、类型定义(typedef)
1.typedef用于定义新的类型名称,而不是定义新的变量或函数。
2.typedef 和 #define的区别:
一般最好使用typedef,部分原因是它能正确处理指针类型,另外就是遵守作用域规则。
typedef char *String_t;
#define String_d char*
String_t s1,s2;
String_d s3,s4;
s1、s2和s3都被定义成char*,但s4却被定义成了char类型。
3.不能在定义typedef类型之前使用它。如下面的代码编译会报错
typedef struct{
char *item;
NODEPTR next;
} *NODEPTR;
改法
①
typedef struct node{
char *item;
struct node *next;
} *NODEPTR;
改法②
struct node;
typedef struct node *NODEPTR;
struct node{
char *item;
NODEPTR next;
}
改法③
struct node{
char* item;
struct node *next;
};
typedef struct node *NODEPTR;
4.定义一对相互引用的结构
方法①
struct a{
int afield;
struct b *bpointer;
};
struct b{
int bfield;
struct a * apointer;
};
方法②
struct a;
struct b;
typedef struct a *APTR;
typedef struct b * BPTR;
struct a{
int afield;
BPTR bpointer;
};
struct b{
int bfield;
APTR apointer;
};
5.函数指针类型的定义
typedef int (*funcptr)();
上面语句定义了一个类型funcptr,表示指向返回值为int型(参数未指明)的函数的指针。
即 funcptr fp1,fp2;和int (*pf1)(),(*pf2)();的写法是等价的。
五、const限定词
1.反例
typedef char *charp;
const charp p;
则p为const(而不是p所指向的字符),就像const int i是将i声明为const原因一样。typedef的替换并不完全是基于文本的。
const int n=5;
int a[n];
编译出错为
3.const char *p,char const *p,char *const p的区别
前两个可以互换,它们声明了一个指向字符常量的指针(就是不能改变所指向的字符值)。
第三个是声明一个指向字符的指针常量,就是不能修改指针。
可以看出const在指针外面,则“常”的是指向的字符;const在指针里面,则“常”的是指针本身。
六、数组大小
1.根据传入的参数决定局部数组的大小
在C99中引入了变长数组(VLA),可以很方便的实现长度未定的局部数组。但是vc++6.0用的是C89,要实现只能用malloc()函数,并在函数返回之前调用free()函数。
动态分配一个二维数组的例子如下:
方法:分配一个指针数组,然后把每个指针初始化为动态分配的“行”。
①行间地址是不连续的
#include
void test(int nrows,int ncolumns);
int main()
{
test(4,5);
system("pause");
return 0;
}
void test(int nrows,int ncolumns)
{
int **array1=malloc(nrows*sizeof(int *));//分配指针数组
int i;
for(i=0;i
输出结果为
②行间地址是连续的#include
void test(int nrows,int ncolumns);
int main()
{
test(4,5);
system("pause");
return 0;
}
void test(int nrows,int ncolumns)
{
int **array2=malloc(nrows*sizeof(int *));//分配指针数组
int i;
array2[0]=malloc(nrows*ncolumns*sizeof(int));//分配整个方块,包括第0行
for(i=1;i
输出结果为
(注:实际应用中应该判断malloc的返回值,并且应该在函数返回之前调用free()函数释放的内存。)
2.获得一个全局数组的大小
在file1.c中
int array[]={1,2,3};
在file2.c中
extern int array[];
如果想在file2.c文件中用sizeof()得到array的大小是得不到的,原因是sizeof在编译时发生作用,不能获得定义在另外一个文件中的数组的大小。
标准C不保证一个对象可以大于32K,或者C99的64K。解决办法有:
①用链表或者结构指针来代替一个更大结构数组。比如:1中的根据传入的参数决定局部数组的大小中行间地址不连续的方法。
②如果使用的是PC兼容机(基于8086)系统,遇到64K或者640K的限制,可以考虑使用“huge”内存模型,或者使用扩充内存或者扩展内存,或使用malloc的变体函数halloc和farmalloc,或者使用32位的“扁平”(flat)编辑器(例如:djgpp),或者使用某种DOS扩充器,或者换一个操作系统。
七、初始化
1.对于没有显示初始化的变量,如果是静态(static)生存期(函数外声明的变量和静态存储变量)的未初始化变量,可以确保整形初始化为0,浮点型初始化为0.0,指针初始化为null。
如果是自动(automatic)生存期的变量(即非静态存储类型的局部变量),值是不确定的垃圾内存。
(注:对于数组和结构,初始化时也被认为“变量”)
#include
int i;
float f;
char *p;
int main()
{
int j;
static int k;
printf("i=%d j=%d k=%d\n",i,j,k);
printf("f=%f\n",f);
printf("p=%d\n",p);
system("pause");
return 0;
}
输出结果为
(注:使用malloc和realloc动态分配的内存也可能含有垃圾数据。用calloc获得的内存全为零,但对于指针和浮点值不一定有用。)
2.malloc函数只能给自动变量(即局部非静态变量)初始化。如下面的程序会报错
#include
char *p=malloc(10);
int main()
{
return 0;
}
出现的错误如下:
char a[]="hello world!";
char *p="hello world!";
下面程序运行后可能会崩溃
#include
int main()
{
char *p="hello world!";
p[1]='i';
return 0;
}
①作为数组初始值(char a[]),它指明该数组中字符的初始值。
②其他情况下,它会先转化为一个无名的静态字符数组,可能会程序存储在只读内存中,这将导致它不能被修改。
4.字符数组初始化时可以用很长的字符串来赋值,但最终只是字符串的前面部分,并且没有'\0'这个结束符。
比如:
char a[3]="abcd";
是合法的。
#include
int test()
{
printf("haha\n");
return 314;
}
int main()
{
int (*p)()=test;
int a=p();
printf("%d\n",a);
system("pause");
return 0;
}
输出结果
6.联合的初始化问题
在原来的ANSI C中,只有联合中的第一个命名成员可以被初始化。
C99引进了“指定初始试”可以用来初始化任意成员。
好了,第一讲的内容到此为止,我们下期再会。