国嵌六:
空结构体的内存:
struct D
{
};
int main(void)
{
struct D d1;
struct D d2;
printf("%d\n",sizeof(struct D));
printf("%d,%0x\n",sizeof(d1),&d1);
printf("%d,%0x\n",sizeof(d2),&d2);
}
0
0,28ff20
0,28ff20
空结构体占据多大内存不同的编译器结果不同,最合理的是一个字节;codeblocks为1个字节。
由结构体产生柔性数组:
- 柔性数组即数组大小待定的数组;例子中的array[]。
- C语言中的结构体的最后一个元素可以是大小未知的数组。
- c语言中可以有结构体产生柔性数组。
#include <stdio.h>
typedef struct _soft_array
{
int len;
int array[]; //不知道大小的情况下默认为0
}SoftArray;
int main()
{
int i = 0;
//printf("%d\n",sizeof(SoftArray));//所以打印出的结果是4
SoftArray* sa=(SoftArray*)malloc(sizeof(SoftArray)+sizeof(int)*10);
sa->len = 10; //指定len后面数组的长度为10
for(i=0; ilen; i++)
{
sa->array[i] = i + 1;
}
for(i=0; ilen; i++)
{
printf("%d\n", sa->array[i]);
}
free(sa);
return 0;
}
1,2,3,4,5,6,7,8,9,10
斐波拉茨序列(杨辉三角):
- 创建柔性数组的结构: typedef struct
- 创建柔性数组: create_soft_array
- 生成斐波拉茨序列: fac
- 释放柔性数组; delete_soft_array
#include <stdio.h>
typedef struct _soft_array
{
int len;
int array[];
}SoftArray;
SoftArray* creat_soft_array(int size)
{
SoftArray* ret=NULL;
if(size>0)
{
ret=(SoftArray*)malloc(sizeof(*ret)+sizeof(*(ret->array))*size);
ret->len=size;
}
return ret;
}
void fac(SoftArray* sa)
{
int i=0;
if(NULL!=sa)
{
if(1==sa->len)
{
sa->array[0]=1;
}
else
{
for(i=2;i<sa->len;i++)
{
sa->array[i]=sa->array[i-1]+sa->array[i-2];
}
}
}
}
void delete_soft_array(SoftArray* sa)
{
free(sa);
}
int main()
{
int i=0;
SoftArray* sa=creat_soft_array(10);
fac(sa);
for(i=0;i<sa->len;i++)
{
printf("%d\n",sa->array[i]);
}
delete_soft_array(sa);
return 0;
}
1,1,2,3,5,8,13,21,34,55
union与struct的区别:
- struct中的每个域在内存中都是独立分配空间;
- union只分配最大域的空间,所有的域共享这个空间;
struct A
{
int a;
int b;
int c;
};
union B
{
int a;
int b;
int c;
};
int main(void)
{
printf("%d\n",sizeof(struct A));
printf("%d\n",sizeof(union B));
}
12,4
nuion的使用受系统大小端的影响:
大端模式:在高地址的地方放低位的值
小端模式:在低地址的地方放低位的值
union C //占用四个字节
{
int i;
char c;
};
int main()
{
union C c;
c.i=1;
printf("%d\n",c.c);
}
大端模式应该是0;小端模式应该是1;
国嵌七:
枚举类型的使用方法:
- enum(枚举类型)是一种自定义的类型;
- enum默认常量在前一个值得基础上依次加1,
- enum类型的变量只能取定义时的离散值。
#include <stdio.h>
#include <malloc.h>
enum Color
{
GREEN, //注意标点符号,第一个不给初始值,默认为0
RED=2,
BLUE
};
int main(void)
{
enum Color c=GREEN;
enum Color b=BLUE;
printf("%d\n",c);
printf("%d\n",b);
}
0,3
枚举类型与#define的区别:
- #define宏常量只是简单的进行值替换,枚举常量是真正意义上的常量。
- #define宏常量无法被调用,枚举常量可以。
- #define宏常量无类型信息,枚举常量是一种特定类型的常量。
typedef的意义:
- typedef用于给一个已经存在的数据类型重命名;
- typedef并没有产生新的类型;
- typedef重定义的类型不能进行unsigned和signed扩展。
typedef与#define的区别:
(1)原理不同
- #define是C语言中定义的语法,是预处理指令,在预处理时进行简单而机械的字符串替换,不作正确性检查,只有在编译已被展开的源程序时才会发现可能的错误并报错。
- typedef是关键字,在编译时处理,有类型检查功能。它在自己的作用域内给一个已经存在的类型一个别名,但不能在一个函数定义里面使用typedef。
(2)功能不同
- typedef用来定义类型的别名,起到类型易于记忆的功能。另一个功能是定义机器无关的类型。如定义一个REAL的浮点类型,在目标机器上它可以获得最高的精度:typedef long double REAL, 在不支持long double的机器上,看起来是这样的,typedef double REAL,在不支持double的机器上,是这样的,typedef float REAL
- #define不只是可以为类型取别名,还可以定义常量、变量、编译开关等。
(3)作用域不同
- #define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,
- typedef有自己的作用域。
(4)对指针的操作不同
#define INTPTR1 int*
typedef int* INTPTR2;
INTPTR1 p1, p2;
INTPTR2 p3, p4;
含义分别为,
声明一个指针变量p1和一个整型变量p2
声明两个指针变量p3、p4
#define INTPTR1 int*
typedef int* INTPTR2;
int a = 1;
int b = 2;
int c = 3;
const INTPTR1 p1 = &a;
const INTPTR2 p2 = &b;
INTPTR2 const p3 = &c;
const INTPTR1 p1是一个常量指针,即不可以通过p1去修改p1指向的内容,但是p1可以指向其他内容。
const INTPTR2 p2是一个指针常量,不可使p2再指向其他内容。因为INTPTR2表示一个指针类型,因此用const限定,表示封锁了这个指针类型。
INTPTR2 const p3是一个指针常量。
国嵌八:
符号的技巧:
那些注释是对的?
#include <stdio.h>
int main()
{
int/*...*/i;
char* s = "abcdefgh //hijklmn";
//一个右斜杠为换行符
//Is it a \
valid comment?
in/*...*/t i;
return 0;
}
运行结果:
- 提示in/*...*/t i;中in未命名;分析:int/*...*/i; 编译器对注释的处理是用空格代替(int i)
- char* s = "abcdefgh //hijklmn"; 注释符号不能出现在“”之间
- //Is it a \ valid comment? 一个右斜杠为换行符
注释规则:
- 编译器会在编译的时候删除掉注释,但不是简单的删除而是用空格代替;
- 编译器认为双引号括起来的内容都是字符串,//也包括;
- “/*....*/”型注释不能被嵌套。
你觉得y=x/*p是什么意思?作者本意:把x除以*p的结果赋值给y。
编译器:将/*作为一段注释的开始,把/* 后面的内容都当成注释内容,直到*/出现为止。在编译器看来,注释和其它程序元素是平等的,因此,作为工程师不能轻视注释,可以用x/ *p表示(空格的重要性)
- 注释应该准确易懂,防止二义性,错误的注释有害而无利;
- 注释是对代码的提示,避免出现臃肿和喧宾夺主;
- 一目了然的代码避免加注释;
- 不要用缩写来注释代码,这样可以产生误解;
- 注释要阐述原因而不是用来描述程序的运行过程。
国嵌九:
接续符和转义符:
#include <stdio.h>
#def\
ine MAX\
255
int main()
{
/\
/这是\
\
注释
i\
n\
t\
*\
p\
= \
NULL;
printf("%0X\n", p);
return 0;
}
0
C语言中的接续符(\)是指示编译器行为的利器。
接续符的使用:
- 编译器会将反斜杠剔除,跟在反斜杠后面的字符自动解到前一行;
- 在接续单词时,反斜杠后不能有空格,反斜杠的下一行之前也不能有空格;
- 接续符适合在定义宏代码块时使用
#include <stdio.h>
#define SWAP(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
}
int main()
{
int a = 1;
int b = 2;
SWAP(a,b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
a=2 b=1
C语言中的转义字符(\)主要用于表示无回显字符,也可以用于表示常规字符:
- C语言中的反斜杠同时具有接续符he转义字符的作用
- 当反斜杠作为接续符使用时可以直接出现在程序中,
- 当反斜杠作为转义符使用时需要出现在字符或者字符串中。
国嵌十:
单引号与双引号:
#include <stdio.h>
int main()
{
char* p1 = 1 ;
char* p2 = '1';
char* p3 = "1";
printf("%s, %s, %s", p1, p2, p3);
printf('\n');
printf("\n");
return 0;
}
运行结果:提示段错误
分析原因:
- char* p1 =1 ;表示将p1这个指针空间赋值1,那么p1指向了地址为1的内存空间;
- char* p2='1';将‘1’对应的ASCLL码(49)赋值给p2指针空间,则p2指向了地址为49的内存空间。这两个地址空间的内存段,都是低地址段,是系统预留的空间,用来拷贝内核代码的使用的,所以上层的应用程序,不能占用这部分空间——段错误
- char* p3="1";p3是指针空间,分配四个字节大小的空间,用来存放地址值,p3指向的内存空间是char类型的,只分配一个字节空间。char* p3="1";此时p3空间存放的值是字符:‘1’和‘\0’。
- printf('\n');由printf函数的原型:printf的第一个参数是一个指针类型的形参,那么将一个字符赋值该变量,出现的问题如上述2。
c语言中的单引号和双引号:
- 单引号:表示字符常量,‘a’表示字符常量;在内存中占一个字节;‘a’+1表示‘a’的ASCLL码+1,结果为‘b’。
- 双引号:表示字符串常量,“a”表示字符串常量;在内存中占两个字节;“a”+1表示指针运算,结果指向“a”的结束符‘\0’。
#include <stdio.h>
int main()
{
char c = " ";
while( c=="\t" || c==" " || c=="\n" )
{
scanf("%c", &c);
}
return 0;
}
本意:输入\t 空格;输入回车会一直循环while;输入其他字符就会退出while。
运行结果:没有循环
分析原因:
将字符串赋值给一个字符将发生:字符串存在内存的某个区域,char c = " ";代表的是内存的地址。假如c=0xAABBCCDD,编译器认为是合理的,但是将c转换成c=0xDD;与判断 while( c=="\t" || c==" " || c=="\n" )相比较不会存在相等,即while语句不执行。
更改:
#include <stdio.h>
int main()
{
char c = ' ';
while( c=='\t' || c==' ' || c=='\n' )
{
scanf("%c", &c);
}
return 0;
}
字符串表示的是字符串的首地址,所以:char abc ="a";实际上abc中存放的是“a”首地址的一个字节的内容(高地址位字节/低地址位字节)。
- 本质上单括号括起来的一个字符代表整数;
- 双引号括起来的字符代表一个指针;
- C编译器接受字符和字符串的比较,可是意义是错误的;
- C编译器允许字符串对字符变量赋值,可是意义是可笑的。