您的位置:首页 > 编程语言 > C语言/C++

C语言数据类型问题及答疑

2017-10-07 17:56 204 查看
1.C语言里面如何计算数据类型取值范围?

Char 8  -128~127 8 -128~127

int   16  -32768~32767 32 -21亿~21亿

short 16 -32768~32767 16 -32768~32767

long 32 -21亿~21亿 32 -21亿~21亿

Unsigned char 8 255 8 255

unsigned int 16 0~65535 32 0~42亿

unsigned short 16 0~65535 16 0~65535 

unsigned long 32 0~42亿 32 0~42亿

2. C语言中数据类型的长度?
  ANSI C99标准中定义了两类(四个)类型修饰符:long/short和unsigned/signed。
  C99标准规定,long类型不能比变通类型短,short类型不能比普通类型长。而unsigned与signed的区别在实现上是有无符号的区别,而是使用上是取值范围的区别,两者表示范围相同,但前者全是正数,后者关于0对称。
  说明:

long/short可以修饰int,long还可以修饰double。
unsigned/signed可以修饰int, char,不可以修饰浮点型。
int长度是机器的字长,short int是半个字长,long int是一个或两个字长。
unsigned/signed长度与普通类型一样,只是表示区间不同。

3.C语言中如何定义全局变量
int f = 7;// 这个是全局变量
int myadd(int a, int b)
{
int c = a + b;// c是局部变量,只能在myadd中使用
return c;
}
int main(void)
{
printf("%d\n", f);// f是全局变量,这句是正确的。
}

先看用来修饰变量的时候。变量在c里面可分为存在全局数据区、栈和堆里。其实我们平时所说的堆栈是栈而不是堆,不要弄混。

int a ;

int main()

{

    int b ; 

    int c* = (int *)malloc(sizeof(int));

}

a是全局变量,b是栈变量,c是堆变量。

static对全局变量的修饰,可以认为是限制了只能是本文件引用此变量。有的程序是由好多.c文件构成。彼此可以互相引用变量,但加入static修饰之后,只能被本文件中函数引用此变量。

static对栈变量的修饰,可以认为栈变量的生命周期延长到程序执行结束时。一般来说,栈变量的生命周期由OS管理,在退栈的过程中,栈变量的生命也就结束了。但加入static修饰之后,变量已经不再存储在栈中,而是和全局变量一起存储。同时,离开定义它的函数后不能使用,但如再次调用定义它的函数时,它又可继续使用, 而且保存了前次被调用后留下的值。

static对函数的修饰与对全局变量的修饰相似,只能被本文件中的函数调用,而不能被同一程序其它文件中的函数调用。

文件a.c

static int i; //只在a文件中用

int j;          //在工程里用

static void init()         //只在a文件中用

{

}

void callme()          //在工程中用

{

    static int sum;

}

文件b.c

extern int j;                    //调用a文件里的

extern void callme();   //调用a文件里的

int main()

{

  ...

}

文件A.cpp调用a.c里面的变量i和函数callme()

extern "C"  //在c++文件里调用c文件中的变量

{

   int j;

   void callme();

}

int main()

{

   callme();

}
B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;
(1)全局变量一般用外部存储方式存储,用保留字extern加以定义。此时,变量的作用域是构成整个程序的所有程序文件,也就是定义的外部变量可供其它程序文件使用。
使用这样的全局变量一定要非常慎重,一旦产生错误,将波及整个程序。
(2)如果希望全局变量仅限于本程序文件使用,而其它程序文件中不能引用,这时必须将其存储方式定义为静态存储方式,用保留字static加以定义。此时称为静态外部变量。
例如,在上例文件filel.c中,如果作这样的定义:
static int a:
则变量a的作用域被缩小至本程序文件filel.c,文件file2.c中不能引用。
值得注意的是对全局变量加static,定义为静态存储方式,并不意味着是静态存储;而不加static,是动态存储。两种形式的全局变量(外部变量)都是静态存储方式,都是编译时分配存储空间,但作用域不同。使用静态外部变量,有利于隔离错误,有利于模块化程序设计。
(3)全局变量的缺省存储方式是外部存储方式。
前面章节中的程序没有见到变量的存储类别定义,实际上采用变量的缺省存储方式。对局部变量采用auto方式,对全局变量采用extern方式。这也是至今为止,我们在程序中没有见到auto、extern等的原因。
至此,我们对变量的存储类别及数据类型进行了全面讨论,在此作个小结。
变量定义的一般形式
存储类别数据类型变量表;
2.变量定义的作用
①规定了变量的取值范围。
②规定了变量进行的运行操作。
③规定了变量的作用域。
④规定了变量的存储方式。
⑤规定了变量占用的存储空间。
局部变量和全局变量
从作用域角度将变量分为局部变量和全局变量。它们采取的存储类别如下:
局部变量:
①自动变量,即动态局部变量(离开函数,值就消失)。
②静态局部变量(离开函数,值仍保留)。
③寄存器变量(离开函数,值就消失)。
④形式参数可以定义为自动变量或寄存器变量。
全局变量:
①静态外部变量(只限本程序文件使用)。
②外部变量(即非静态的外部变量,允许其它程序文件引用)。
动态存储和静态存储
从变量存在时间可将变量存储分为动态存储和静态存储。静态存储是在整个程序运行时都存在,而动态存储则是在调用函数时临时分配存储单元。
动态存储:
①自动变量(函数内有效)。
②寄存器变量(函数内有效)。
③形式参数。
静态存储:
①静态局部变量(函数内有效)。
②静态外部变量(本程序文件内有效)。
③外部变量(整个程序可引用)。
静态存储区和动态存储区
从变量值存放的位置可将变量存储区分为静态存储区和动态存储区:
内存中静态存储区:
①静态局部变量。
②静态外部变量。
③外部变量(可被同一程序其它文件引用)。
内存中动态存储区:自动变量和形式参数。
CPU中的寄存器:寄存器变量。

extern修饰变量的声明。举例来说,如果文件a.c需要引用b.c中变量int v,就可以在a.c中声明extern int v,然后就可以引用变量v。这里需要注意的是,被引用的变量v的链接属性必须是外链接(external)的,也就是说a.c要引用到v,不只是取决于在a.c中声明extern
int v,还取决于变量v本身是能够被引用到的。这涉及到c语言的另外一个话题--变量的作用域。能够被其他模块以extern修饰符引用到的变量通常是全局变量。还有很重要的一点是,extern int v可以放在a.c中的任何地方,比如你可以在a.c中的函数fun定义的开头处声明extern int v,然后就可以引用到变量v了,只不过这样只能在函数fun作用域中引用v罢了,这还是变量作用域的问题。对于这一点来说,很多人使用的时候都心存顾虑。好像extern声明只能用于文件作用域似的。
2. extern修饰函数声明。从本质上来讲,变量和函数没有区别。函数名是指向函数二进制块开头处的指针。如果文件a.c需要引用b.c中的函数,比如在b.c中原型是int fun(int mu),那么就可以在a.c中声明extern int fun(int mu),然后就能使用fun来做任何事情。就像变量的声明一样,extern int fun(int mu)可以放在a.c中任何地方,而不一定非要放在a.c的文件作用域的范围中。对其他模块中函数的引用,最常用的方法是包含这些函数声明的头文件。使用extern和包含头文件来引用函数有什么区别呢?extern的引用方式比包含头文件要简洁得多!extern的使用方法是直接了当的,想引用哪个函数就用extern声明哪个函数。这大概是KISS原则的一种体现吧!这样做的一个明显的好处是,会加速程序的编译(确切的说是预处理)的过程,节省时间。在大型C程序编译过程中,这种差异是非常明显的。
3. 此外,extern修饰符可用于指示C或者C++函数的调用规范。比如在C++中调用C库函数,就需要在C++程序中用extern “C”声明要引用的函数。这是给链接器用的,告诉链接器在链接的时候用C函数规范来链接。主要原因是C++和C程序编译完成后在目标代码中命名规则不同。
     首先需要注意的是,const修饰的是在它前面的类型,如果它前面没有类型,那它修 饰的是紧跟着它的那个类型。 例如:
(a)const int i = 0; 和 (b)int const i = 0; 是完全一样的。
在(a)中,const前面没有类型,它就修饰它后面的那个int类型。在(b)中,const修饰它前 面的int类型,两者没有任何区别。
再看另一个稍复杂一点的例子,下面两条语句却不相同: (c)const int *pi = 0;
/* 相当于int const *pi = 0; pi是一个指向const int的指针,复引用此运算符为得到一 个const int的类型,该类型不能作为左值,在该语句后使用类似于*pi = 1的操作将导致 编译错误。但该变量本身并不具备const属性,可以使用pi = &i的操作。可用于访问只读 存储器。*/ 
(d)int* const pi = 0;
/* pi是一个指向int类型的const指针,复引用此运算符为得到一个int类型,该类型可以 作为左值,在该语句可以使用类似于*pi = 1的操作,但该变量本身具备const属性,使用 pi = &i的操作将导致编译错误。可用于访问固定位置的存储器。*/ 再看一个更复杂的例子:
(e)const int* const pi = 0;
/* pi和*pi均不能作为左值。它只适合于读取某个固定位置的只读存储器 */ 

const还有下列典型用法:
     * 用于参数列表,通常修饰的是指针类型,表明该函数不会试图对传入的地址进行写 操作。例如:
void *memcpy(void *, const void *, size_t);

     * 用于返回值,通常是一个指向只读区域的指针。例如: const datatype_t *get_fixed_item(int index);
     * 给固定不变的数据(例如码表)加上只读属性,在某些情况下可以减小ram的开销。   

void (*b[10]) (void (*)()); //#2 

double(*)() (*pa)[9];          //#3

C语言中函数声明和数组声明。函数声明一般是这样: 

int fun(int, double); 

对应函数指针(pointer to function)的声明是这样: 

int (*pf)(int, double); 

可以这样使用: 

pf = &fun;       //赋值(assignment)操作 

(*pf)(5, 8.9);//函数调用操作 

也请注意,C语言本身提供了一种简写方式如下: 

pf = fun;        // 赋值(assignment)操作 

pf(5, 8.9);      // 函数调用操作 

不过我本人不是很喜欢这种简写,它对初学者带来了比较多的迷惑。 

数组声明一般是这样: 

int a[5]; 

对于数组指针(pointer to array)的声明是这样: 

int (*pa)[5]; 

可以这样使用: 

pa = &a;             // 赋值(assignment)操作 

int i = (*pa)[2]; // 将a[2]赋值给i;

#1:int* (*a[5])(int, char*); 

首先看到标识符名a,“[]”优先级大于“*”,a与“[5]”先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针, 

指针指向“(int, char*)”,对,指向一个函数,函数参数是“int, char*”,返回值是“int*”。完毕,我们干掉了第一个纸老虎。:)

b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是“void (*)()”【注1】,返回值是“void”。完毕! 

注1:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是“void”。

#3:double(*)()(*pa)[9]; 

pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是“double(*)()”【也即一个指针,指向一个函数,函数参数为空,返回值是“double

#1:int* (*a[5])(int, char*); 

typedef int* (*PF)(int, char*);//PF是一个类型别名【注2】。 

PF a[5];//跟int* (*a[5])(int, char*);的效果一样! 

注2:很多初学者只知道typedef char* pchar;但是对于typedef的其它用法不太了解。Stephen Blaha对typedef用法做过一个总结:“建立一个类型别名的方法很简单,在传统的变量声明表达式里用类型名替代变量名,然后把关键字typedef加在该语句的开头”。

#2:void (*b[10])(void (*)()); 

typedef void (*pfv)(); 

typedef void (*pf_taking_pfv)(pfv); 

pf_taking_pfv b[10]; //跟void (*b[10]) (void (*)());的效果一样!

#3. double(*)()(*pa)[9]; 

typedef double(*PF)(); 

typedef PF (*PA)[9]; 

PA pa; //跟doube(*)()(*pa)[9];的效果一样!

在这里我只说const,volatile是一样的!【注3】 

注3:顾名思义,volatile修饰的量就是很容易变化,不稳定的量,它可能被其它线程,操作系统,硬件等等在未知的时间改变, 

所以它被存储在内存中,每次取用它的时候都只能在内存中去读取,它不能被编译器优化放在内部寄存器中。 

类型声明中const用来修饰一个常量,我们一般这样使用:const在前面: 

const int; //int是const 

const char*;//char是const 

char* const;//*(指针)是const 

const char* const;//char和*都是const 

对初学者,const char*和 char* const是容易混淆的。这需要时间的历练让你习惯它。 上面的声明有一个对等的写法:const在后面: 

int const; //int是const 

char const*;//char是const 

char* const;//*(指针)是const 

char const* const;//char和*都是const

第一次你可能不会习惯,但新事物如果是好的,我们为什么要拒绝它呢?:)const在后面有两个好处: 

A.const所修饰的类型正好是在它前面的那一个。如果这个好处还不能让你动心的话,那请看下一个! 

B.我们很多时候会用到typedef的类型别名定义。比如typedef char* pchar,如果用const来修饰的话,

是不是让你大吃一惊!但如果你采用const在后面的写法,意义就怎么也不会变,不信你试试! 

不过,在真实项目中的命名一致性更重要。你应该在两种情况下都能适应,并能自如的转换,公司习惯, 

商业利润不论在什么时候都应该优先考虑!不过在开始一个新项目的时候,你可以考虑优先使用const在后面的习惯用法。

二.Typedef声明有助于创建平台无关类型,甚至能隐藏复杂和难以理解的语法。 

   不管怎样,使用 typedef 能为代码带来意想不到的好处,通过本文你可以学习用typedef避免缺欠,从而使代码更健壮。 

typedef声明,简称typedef,为现有类型创建一个新的名字。比如人们常常使用 typedef 来编写更美观和可读的代码。 

所谓美观,意指typedef 能隐藏笨拙的语法构造以及平台相关的数据类型,从而增强可移植性和以及未来的可维护性。 

本文下面将竭尽全力来揭示 typedef 强大功能以及如何避免一些常见的陷阱,如何创建平台无关的数据类型,隐藏笨拙且难以理解的语法. 

typedef使用最多的地方是创建易于记忆的类型名,用它来归档程序员的意图。类型出现在所声明的变量名字中,位于typedef关键字右边。 

例如:typedef int size; 

此声明定义了一个 int 的同义字,名字为 size。注意typedef并不创建新的类型。它仅仅为现有类型添加一个同义字。 

你可以在任何需要 int 的上下文中使用 size: 

void measure(size * psz); 

size array[4]; 

size len = file.getlength(); 

typedef 还可以掩饰复合类型,如指针和数组。例如,你不用象下面这样重复定义有81个字符元素的数组: 

char line[81]; char text[81]; 

定义一个typedef,每当要用到相同类型和大小的数组时,可以这样: 

typedef char Line[81]; 

Line text, secondline; 

getline(text); 

同样,可以象下面这样隐藏指针语法: 

typedef char * pstr; 

int mystrcmp(pstr, pstr); 

这里将带我们到达第一个 typedef 陷阱。标准函数 strcmp()有两个const char *类型的参数。因此,它可能会误导人们象下面这样声明: 

int mystrcmp(const pstr, const pstr); 

这是错误的,事实上,const pstr被编译器解释为char * const(一个指向 char 的常量指针),而不是const char *(指向常量 char 的指针)。 

这个问题很容易解决: 

typedef const char * cpstr; 

int mystrcmp(cpstr, cpstr); 

上面讨论的 typedef 行为有点像 #define 宏,用其实际类型替代同义字。不同点是typedef在编译时被解释 

,因此让编译器来应付超越预处理器能力的文本替换。例如: 

typedef int (*PF) (const char *, const char *); 

这个声明引入了 PF 类型作为函数指针的同义字,该函数有两个 const char * 类型的参数以及一个 int 类型的返回值。如果要使用下列形式的函数声明,那么上述这个 typedef 是不可或缺的: 

PF Register(PF pf); 

Register()的参数是一个PF类型的回调函数,返回某个函数的地址,其署名与先前注册的名字相同。做一次深呼吸。下面我展示一下如果不用 typedef,我们是如何实现这个声明的: 

int (*Register (int (*pf)(const char *, const char *))) (const char *, const char *); 

很少有程序员理解它是什么意思,更不用说这种费解的代码所带来的出错风险了。显然,这里使用 typedef 不是一种特权, 

而是一种必需。typedef 就像 auto,extern,mutable,static,和 register 一样,是一个存储类关键字。 

这并不是说typedef会真正影响对象的存储特性;它只是说在语句构成上,typedef 声明看起来象 static,extern 等类型的变量声明。 

下面将带到第二个陷阱: 

typedef register int FAST_COUNTER; // 错误编译通不过 

问题出在你不能在声明中有多个存储类关键字。因为符号 typedef 已经占据了存储类关键字的位置, 

在 typedef 声明中不能用 register(或任何其它存储类关键字)。typedef 有另外一个重要的用途,那就是定义机器无关的类型, 

例如,你可以定义一个叫 REAL 的浮点类型,在目标机器上它可以获得最高的精度:

在不支持 long double 的机器上,该 typedef 看起来会是下面这样: 

typedef double REAL; 

并且,在连 double 都不支持的机器上,该 typedef 看起来会是这样: 

typedef float REAL; 

你不用对源代码做任何修改,便可以在每一种平台上编译这个使用 REAL 类型的应用程序。唯一要改的是 typedef 本身。 

在大多数情况下,甚至这个微小的变动完全都可以通过奇妙的条件编译来自动实现。不是吗? 

标准库广泛地使用 typedef 来创建这样的平台无关类型:size_t,ptrdiff 和 fpos_t 就是其中的例子。 

此外,象 std::string 和 std::ofstream 这样的 typedef 还隐藏了长长的,难以理解的模板特化语法, 

例如:basic_string,allocator> 和 basic_ofstream>。

定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如: 

char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针, 

// 和一个字符变量; 

以下则可行: 

typedef char* PCHAR; // 一般用大写 

PCHAR pa, pb; // 可行,同时声明了两个指向字符变量的指针 

虽然: 

char *pa, *pb; 

也可行,但相对来说没有用typedef的形式直观,尤其在需要大量指针的地方,typedef的方式更省事。

用在旧的C代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如: 

struct tagPOINT1 



int x; 

int y; 

}; 

struct tagPOINT1 p1;

tagPOINT1 p1;

typedef struct tagPOINT 



int x; 

int y; 

}POINT;

用typedef来定义与平台无关的类型。 

比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为: 

typedef long double REAL; 

在不支持 long double 的平台二上,改为: 

typedef double REAL; 

在连 double 都不支持的平台三上,改为: 

typedef float REAL; 

也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。 

标准库就广泛使用了这个技巧,比如size_t。 

另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。

为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:

变量名为a,直接用一个新别名pFun替换a就可以了: 

typedef int *(*pFun)(int, char*); 

原声明的最简化版: 

pFun a[5];

变量名为b,先替换右边部分括号里的,pFunParam为别名一: 

typedef void (*pFunParam)(); 

再替换左边的变量b,pFunx为别名二: 

typedef void (*pFunx)(pFunParam); 

原声明的最简化版: 

pFunx b[10];

变量名为e,先替换左边部分,pFuny为别名一: 

typedef double(*pFuny)(); 

再替换右边的变量e,pFunParamy为别名二 

typedef pFuny (*pFunParamy)[9]; 

原声明的最简化版: 

pFunParamy e;

int (*func)(int *p); 

首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(*func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int*类型的形参,返回值类型是int。 

int (*func[5])(int *); 

func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个*,说明func的元素是指针(注意这里的*不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比*高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int*类型的形参,返回值类型为int。

type (*)(....)函数指针 

type (*)[]数组指针 

---------------------------------

记住,typedef是定义了一种类型的新别名,不同于宏,它不是简单的字符串替换。比如: 

先定义: 

typedef char* PSTR; 

然后: 

int mystrcmp(const PSTR, const PSTR);

原因在于const给予了整个指针本身以常量性,也就是形成了常量指针char* const。 

简单来说,记住当const和typedef一起出现时,typedef不会是简单的字符串替换就行。

typedef在语法上是一个存储类的关键字(如auto、extern、mutable、static、register等一样),虽然它并不真正影响对象的存储特性,如: 

typedef static int INT2; //不可行 

编译将失败,会提示“指定了一个以上的存储类”。

现在是不是觉得要认识它们是易如反掌,工欲善其事,必先利其器!我们对这种表达方式熟悉之后,就可以用“typedef”来简化这种类型声明。
9.枚举类型(Enum)是什么
union成员共享同一块大小的内存,一次只能使用其中的一个成员。 
对某一个成员赋值,会覆盖其他成员的值(也不奇怪,因为他们共享一块内存。但前提是成员所占字节数相同,当成员所占字节数不同时只会覆盖相应字节上的值,比如对char成员赋值就不会把整个int成员覆盖掉,因为char只占一个字节,而int占四个字节)

10.C语言中关于float、double、long double精度及数值范围应该如何理解?

float和double的精度是由尾数的位数来决定的。浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,由于它是不变的,故不能对精度造成影响。float:2^23
= 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;double:2^52
= 4503599627370496,一共16位,同理,double的精度为15~16位。

其中负指数决定了浮点数所能表达的绝对值最小的非零数;而正指数决定了浮点数所能表达的绝对值最大的数,也即决定了浮点数的取值范围。float的范围为-2^128
~ +2^128,也即-3.40E+38 ~ +3.40E+38;double的范围为-2^1024
~ +2^1024,也即-1.79E+308 ~+1.79E+308。


11.typedef的用法

一.基本概念剖析
int* (*a[5])(int, char*);       //#1 

2.有了上面的基础,我们就可以对付开头的三只纸老虎了!:) 这个时候你需要复习一下各种运算符的优先顺序和结合顺序了,顺便找本书看看就够了。 

#2:void (*b[10]) (void (*)()); 

--------------------------------------------------------------- 

3.const和volatile在类型声明中的位置。 

当const在前面的时候,就是const pchar,你会以为它就是const char* ,但是你错了,它的真实含义是char* const。 

typedef long double REAL; 

用途一: 

用途二: 

而在C++中,则可以直接写:结构名 对象名,即: 

估计某人觉得经常多写一个struct太麻烦了,于是就发明了: 

POINT p1; // 这样就比原来的方式少写了一个struct,比较省事,尤其在大量使用的时候
或许,在C++中,typedef的这种用途二不是很大,但是理解了它,对掌握以前的旧代码还是有帮助的,毕竟我们在项目中有可能会遇到较早些年代遗留下来的代码。
用途三: 

用途四: 

1. 原声明:int *(*a[5])(int, char*); 

2. 原声明:void (*b[10]) (void (*)()); 

3. 原声明:doube(*)() (*e)[9]; 

理解复杂声明可用的“右左法则”:从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例: 

也可以记住2个模式: 

陷阱一: 

const PSTR实际上相当于const char*吗?不是的,它实际上相当于char* const。 

陷阱二: 

”】。(注意typedef int* p[9]与typedef int(*p)[9]的区别,前者定义一个数组,此数组包含9个int*类型成员,而后者定义一个指向数组的指针,被指向的数组包含9个int类型成员)。 

(1) 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
(2) DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
(3) 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
(4) 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
(5) 枚举型是预处理指令#define的替代。
(6) 类型定义以分号;结束。
12.联合体是什么

1、union中可以定义多个成员,union的大小由最大的成员的大小决定。 

4、联合体union的存放顺序是所有成员都从低地址开始存放的。
(1) 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
(2) DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
(3) 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
(4) 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
(5) 枚举型是预处理指令#define的替代。
(6) 类型定义以分号;结束。
(1) 枚举型是一个集合,集合中的元素(枚举成员)是一些命名的整型常量,元素之间用逗号,隔开。
(2) DAY是一个标识符,可以看成这个集合的名字,是一个可选项,即是可有可无的项。
(3) 第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
(4) 可以人为设定枚举成员的值,从而自定义某个范围内的整数。
(5) 枚举型是预处理指令#define的替代。
(6) 类型定义以分号;结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言