【c语言问题系列教程之一】变量声明和初始化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MyLinChi/article/details/52652595

一、基本类型

1.c语言整数类型的大小并没有精确的定义,而是随着编译器的类型而变化,ANSI C能保证的只是数据的最小尺寸。char>=8位,short>=16位,int>=16位,long>=32位。

2.因此,可以用typedef定义int16和int32,然后根据实际的机器环境把它们定义为int,short,long等类型。但是,标准头文件<inttypes.h>已经定义了标准名称int16_t,uint32_t等。

二、指针声明

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类型。
而#define的使用一般是配合#ifdef来构成条件编译。

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的替换并不完全是基于文本的。
2.反例

const int n=5;
int a[n];

编译出错为


const限定词的真正含义是“只读”,用它限定的对象通常是运行是不能被赋值的对象。因此用const限定的对象的值并不完全是一个真正的常量,不 能用作数组维度、case行标或者类似的环境。(注:在c++中是可以用作数组的维度等的。)

3.const char *p,char const *p,char *const p的区别

前两个可以互换,它们声明了一个指向字符常量的指针(就是不能改变所指向的字符值)。

第三个是声明一个指向字符的指针常量,就是不能修改指针。

可以看出const在指针外面,则“常”的是指向的字符;const在指针里面,则“常”的是指针本身

六、数组大小

1.根据传入的参数决定局部数组的大小

在C99中引入了变长数组(VLA),可以很方便的实现长度未定的局部数组。但是vc++6.0用的是C89,要实现只能用malloc()函数,并在函数返回之前调用free()函数。

动态分配一个二维数组的例子如下:

方法:分配一个指针数组,然后把每个指针初始化为动态分配的“行”。

①行间地址是不连续的

#include<stdio.h>
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<nrows;i++)
	{
		array1[i]=malloc(ncolumns*sizeof(int));	
	}
	array1[nrows-1][ncolumns-1]=314;
	printf("array1[nrows-1][ncolumns-1] is %d\n",array1[nrows-1][ncolumns-1]);
}

输出结果为


②行间地址是连续的

#include<stdio.h>
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<nrows;i++)
	{
		array2[i]=array2[0]+i*ncolumns;	//把每一行的首地址赋值给array2指针
	}
	array2[nrows-1][ncolumns-1]=314;
	printf("array1[nrows-1][ncolumns-1] is %d\n",array2[nrows-1][ncolumns-1]);
}
输出结果为


(注:实际应用中应该判断malloc的返回值,并且应该在函数返回之前调用free()函数释放的内存。)

2.获得一个全局数组的大小

在file1.c中

int array[]={1,2,3};
在file2.c中

extern int array[];
如果想在file2.c文件中用sizeof()得到array的大小是得不到的,原因是sizeof在编译时发生作用,不能获得定义在另外一个文件中的数组的大小。
解决的办法可以在file1.c中在声明一个整型变量表示数组的大小。
3.关于分配大于64K的数组或结构的问题

标准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<stdio.h>
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<stdio.h>
char *p=malloc(10);
int main()
{
	return 0;
}

出现的错误如下:


3.指针和数组初始化的不同

char a[]="hello world!";
char *p="hello world!";
下面程序运行后可能会崩溃

#include<stdio.h>
int main()
{
	char *p="hello world!";
	p[1]='i';
	return 0;
}

①作为数组初始值(char a[]),它指明该数组中字符的初始值。

②其他情况下,它会先转化为一个无名的静态字符数组,可能会程序存储在只读内存中,这将导致它不能被修改。
4.字符数组初始化时可以用很长的字符串来赋值,但最终只是字符串的前面部分,并且没有'\0'这个结束符。

比如:

char a[3]="abcd";
是合法的。
5.函数指针的初始化

#include<stdio.h>
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引进了“指定初始试”可以用来初始化任意成员。

好了,第一讲的内容到此为止,我们下期再会。









猜你喜欢

转载自blog.csdn.net/MyLinChi/article/details/52652595