C语言超级经典学习笔记
C语言补习笔计
第一天 (2)
第二天 (3)
Printf() (4)
Ascii: (4)
词法符号 (5)
指针理解: (5)
关键字static的作用是什么? (6)
关键字const是什么含意? (8)
关键字volatile有什么含意 (10)
第三天逻辑及运算符号 (11)
基本数据类型 (11)
强制数据类型转换: (11)
运算符: (12)
原码、反码、补码,计算机中负数的表示 (13)
第四天 (15)
优先级 (15)
Switch (16)
有符号数与无符号数之间运算问题 (16)
第五天 (16)
数组: (17)
内存思想 (18)
第六天 (18)
第七天函数 (21)
第八天指针复习 (21)
二维指针示例: (22)
第一天
一、学习方法:how to do,when to do(why to do)
二、vmware:setting->mem network(桥接,NAT)
三、共享:
1、win---win【\\IP】IP是win dest
2、win---linux
2.1vmware提供一个工具vmtools。虚拟的linux具备共享的特性.在虚拟机
设置中的options中有“shared folders”中,在右边选“always
enabeled”然后在下面设置其在windows中的文件夹路径,然后此文件
夹在对应的linux中的应射路径为/mnt/hgfs/***
2.2、若要用此功能要求安装vmtools软件,软件包vmtools.rpm安装方法
[rpm-ivh*.rpm]file*
【环境:要求linux虚拟机必须有内核头文件】fc5
2.3、必须本地的win本地的虚拟机
3、c/s:网络共享
1、设置windows-的共享文件夹【注意安全】,注意打开“充许更改我的
文件名”不然告成vi中和序无法写。
2、linux挂载:mount-t cifs(linux2.6//2.4--vfat)-o
username=*,password=*//IP/shared/home
注意:以上命令中如果是简单共享模式
四、远程登录:
1、sercurCRT[ssh2]telnet
2、ftp、samba
3、linux---linux:NFS
五、学习命令
ls cd rm cp lwd(查看当前目录路径)
六、用“file”查看文件属性
./build ls:(env,PATH)file
七、vi:命令模式,输入模式
切换:i o a s ESC
退出命令:<:wq!>
常用命令:dd yy nyy p r?R*
翻页:G nG<:set nu><:set nonu>$^
!sp文件及路径可以在一个VI中打开多个文件
通过上学习可知,linux开发原码输入可在windows下用ultraedi或souceinsight等软件编写后再通过以上共享方法在linux系统下编译。或都在linux下直接用vi或vim等工具输入。
八、其它:
哈弗结构,数据存储器和和序存储器分开,冯诺依曼没分开。
更改启动方式:
Redhat:在etc/inittab中改例如:vi etc/inittab
重启网络etc/init.d/network restart
编译*.c文件:gcc–o***(生成目标文件的名字)*.C
生成的文件运行方法:在当前文件目录下直接输入文件名称
“./文件名”
vmware下克隆虚拟机:etc/sysconfig/network-scripts/ifcfg-eth0下查mac 地址,改虚拟机目录下的*.vxm中的mac地址使之对应,重启网络。
第二天
程序在内存上运行,实质是在CPU内的寄存器里运行。
ROM
EPROM
Flash:
Nand flash:容量很大,程序不能直接在上面运行。芯片结构决定。
Nor flash:容量不易做大,程序可以直接在上面运行。
1K=2^101M=2^201G=2^30
1Bit=8bit
Int*p则sizeof(P)决定于所在CPU的寻址长度。
每一条汇编语言唯一的机器语言。
Lsb小端模式把数据低位放地址低位。
C语言中所有“#”开头的都是预处理。
#define预处理只做简单的文本替换,在使用时注意最好在每一级加上括号,
#include
/user/include
“”表示在当前目录下寻找该文件,若找不到则在系统默认目录下查找。
Printf()
%p打地址
%02d表示不足2位补0
%d有符号十进制整数
%u无符号十进制整数
%x无符号十六进制整数
%0无符号八进制整数
%c打印一个字符
%s打印一个字符串,遇到‘\0’则结速打应
%p显示一个地址
%f输出一个实数
Mkdir创建目录,vim文件名创建文件
C语中只有printf时才做四舍五入,其它如强制转换做精度丢失,截掉末位如3.124变为3.
C语言没有二进制常量。
Ascii:
第一部分由00H到1FH共32个,一般用来通讯作为控制用
第二部分由20H到7FH共96个,表示键盘上字符数字
第三部分由80H到0FFH共128个,扩展字符
由键盘输入的为字符类型,
Windows中回车ascii为0d0a
Linux中回车ascii为
0x0a:回车0x30:‘0’,‘A’<’a’,‘2’–‘0’=2.
转义字符:
\n回车换行
\t水平到下一一制表位置
\b向前退格
\ddd1~3位八制制数所代表的字符
\xhh1~2位十六进制数所代表的字符
词法符号
关键字:
Const只读变量
Const int*a;a指针对像为只读,a的值可改
Int*const a;a的值为只读,a指向的对像为可改
Enum枚举
Extern外部申名所有函数外不加其它修饰默认为extern
Sizof判断占用内存大小
Typedef起别名
Voliate此关建字只在嵌入式编程中使用,告诉编译器不要进行优化。
指针理解:
用变量a给出下面的定义
a)一个整型数(An integer)。
int a;//An integer
b)一个指向整型数的指针(A pointer to an integer)。
int*a;//A pointer to an integer
c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a
pointer to an intege)r。
int**a;//A pointer to a pointer to an integer
d)一个有10个整型数的数组(An array of10integers)。
int a[10];//An array of10integers
e)一个有10个指针的数组,该指针是指向一个整型数的(An array of10
pointers to integers)。
int*a[10];//An array of10pointers to integers
f)一个指向有10个整型数数组的指针(A pointer to an array of10
integers)。
int(*a)[10];//A pointer to an array of10integers
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer
to a function that takes an integer as an argument and returns an
integer)。
int(*a)(int);//A pointer to a function a that
takes an integer argument and returns an integer。
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并
返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)。
int(*a[10])(int);//An array of10pointers to functions that take an integer argument and return an integer
关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。
大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数
据和代码范围的好处和重要性。
4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因
而只能访问类的static成员变量。
在C语言中,static的字面意思很容易把我们导入歧途,其实它的作用有三条。(1)先来介绍它的第一条也是最重要的一条:隐藏。
当我们同时编译多个文件时,所有未加static前缀的全局变量和函数都具有全局可见性。为理解这句话,我举例来说明。我们要同时编译两个源文件,一个是a.c,另一个是main.c。
下面是a.c的内容
char a='A';//global variable
void msg()
{
printf("Hello\n");
}
下面是main.c的内容
int main(void)
{
extern char a;//extern variable must be declared before use
printf("%c",a);
(void)msg();
return0;
}
程序的运行结果是:
A Hello
你可能会问:为什么在a.c中定义的全局变量a和函数msg能在main.c中使用?前面说过,所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的。
如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c 就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。
(2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。虽然这种用法不常见,但我还是举一个例子。
#i nclude
int fun(void){
static int count=10;//事实上此赋值语句从来没有执行过
return count--;
}
int count=1;
int main(void)
{
printf("global\t\tlocal static\n");
for(;count<=10;++count)
printf("%d\t\t%d\n",count,fun());
return0;
}
程序的运行结果是:
global local static
110
29
38
47
56
65
74
83
92
101
(3)static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00,某些时候这一特点可以减少程序员的工作量。比如初始化一个稀疏矩阵,我们可以一个一个地把所有元素都置0,然后把不是0的几个元素赋值。如果定义成静态的,就省去了一开始置0的操作。再比如要把一个字符数组当字符串来用,但又觉得每次在字符数组末尾加’\0’太麻烦。如果把字符串定义成静态的,就省去了这个麻烦,因为那里本来就是’\0’。不妨做个小实验验证一下。
#i nclude
int a;
int main(void)
{
int i;
static char str[10];
printf("integer:%d;string:(begin)%s(end)",a,str);
return0;
}
程序的运行结果如下
integer:0;string:(begin)(end)
最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。
关键字const是什么含意?
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)如果应试者能正确回答这个问题,我将问他一个附加的问题:下面的声明都是什么意思?
const int a;
int const a;
const int*a;
int*const a;
int const*a const;
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1).关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2).通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3).合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
关键字volatile有什么含意
并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1).并行设备的硬件寄存器(如:状态寄存器)
2).一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3).多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile 变量。不懂得volatile内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1).一个参数既可以是const还可以是volatile吗?解释为什么。
2).一个指针可以是volatile吗?解释为什么。
3).下面的函数有什么错误:
int square(volatile int*ptr)
{
return*ptr**ptr;
}
下面是答案:
1).是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const 因为程序不应该试图去修改它。
2).是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3).这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr 指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int*ptr)
{
int a,b;
a=*ptr;
b=*ptr;
return a*b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int*ptr)
{
int a;
a=*ptr;
return a*a;
第三天逻辑及运算符号
计算机最小处理单位为一个字节不是一个bit
整个C语言中只有=,++,--才能改变变量本身。
Char a类型字符使用时注意上限为255超过后在内存中存的是a%256
Vi下看行号set nu
Printf(“%s,%s,%d”,__FILE__,__LINE__,__FUNCTION__)
基本数据类型
逻辑类型:
整数类型:char指一个系统的最小处理单位1个字节
-1表示为0xff,对10000001后7位取反加1
范围-128~127,在32位裸机系统中使用char表面可以节约内
存,但实际浪费CPU资源。
Long指一个系统的最大处理单位4个字节
short在不同系统中变化,unsigned short范围0~655352字节
int裸板程序与CPU数据总线
在OS系统中跟编译器数据处理相关
一般为4字节
Sizeof在以语言中是关建字,没有头文件也可以用,以查看以上类型共占多少字节。字符串最后都以‘\0’或0‘0’内存中为30
字符串为常量,“”编译器自动分配了连续空间,同时末尾加‘\0’.
强制数据类型转换:
地址类型的转换:
Int a=0x11111111
Char*b=(char*)a
(数据类型名称)《表达示》
运算符:
除法“/”
取模或求余“%”:
用法:循环队列
M进制的数
产生一个在0到M-1之类的数
++--
Int a=10
Int b=(++a)+(++a)
结果b=24
++在变量前则先运算再取变量值,若++在变量后则先取变量的值日再加
C语言中不能用实数的地方只有两处一种是%一种是switch()括号内
的值。
逻辑运算的结果只能为1和0
&&,||运算前后的表达示位置交换后的影响
&&前的表达示为假时后一个表达示将不再运行
||前一表达示为真时后一个表达示将不再运行。
Int a=~0;即a=-1.
移位操作:
<<,>>移位时不移符号位。即负数移动后扔为负数。
a左移N位即表示a*2^n
a右移n位即表示a/2^n
有符号数在移位时不是补‘0’,而是补符号位。
异或:
不同为1相同为0
A^0=A
不用新增第三变量交两整数值
Int a=10,b=20;
a=a^b;
b=a^b;
a=a^b;
后a,b值交换。
奇校验同或
偶校验异或
标准main函数:Int main(int argc,char**argv)
A=x>=y?3:2;A的结果为2,而不是(x>=y),=付值运算的优先级最低。原码、反码、补码,计算机中负数的表示
原码:将一个整数,转换成二进制,就是其原码。如单字节的5的原码为:00000101;-5的原码为10000101。
反码:正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。如单字节的5的反码为:00000101;-5的反码为11111010。
补码:正数的补码就是其原码;负数的反码+1就是补码。如单字节的5的补码为:00000101;-5的原码为11111011。
在计算机中,正数是直接用原码表示的,如单字节5,在计算机中就表示为:00000101。负数用补码表示,如单字节-5,在计算机中表示为11111011。
这儿就有一个问题,为什么在计算机中,负数用补码表示呢?为什么不直接用原码表示?如单字节-5:10000101。
我想从软件上考虑,原因有两个:
1、表示范围
拿单字节整数来说,无符号型,其表示范围是[0,255],总共表示了256个数据。有符号型,其表示范围是[-128,127]。
先看无符号,0表示为00000000,255表示为11111111,刚好满足了要求,可以表示256个数据。
再看有符号的,若是用原码表示,0表示为0000000。因为咱们有符号,所以应该也有个负0(虽然它还是0):10000000。
那我们看看这样还能够满足我们的要求,表示256个数据么?
正数,没问题,127是01111111,1是00000001,当然其它的应该也没有问题。
负数呢,-1是10000001,那么把负号去掉,最大的数是1111111,也就是127,所以负数中最小能表示的数据是-127。
这样似乎不太对劲,该如何去表示-128?貌似直接用原码无法表示,而我们却有两个0。
如果我们把其中的一个0指定为-128,不行么?这也是一个想法,不过有两个问题:一是它与-127的跨度过大;二是在用硬件进行运算时不方便。
所以,计算机中,负数是采用补码表示。如单字节-1,原码为10000001,反码为11111110,补码为11111111,计算机中的单字节-1就表示为11111111。
单字节-127,原码是11111111,反码10000000,补码是10000001,计算机中单字节-127表示为10000001。
单字节-128,原码貌似表示不出来,除了符号为,最大的数只能是127了,其在计算机中的表示为10000000。
2、大小的习惯(个人观点)
也可以从数据大小上来理解。还是以单字节数据为例。有符号数中,正数的范围是[1,127],最大的是127,不考虑符号为,其表示为1111111;最小的是1,不考虑符号为,其表示为0000001。
负数中,最大的是-1,我们就用1111111表示其数值部分。后面的数据依次减1。减到0000001的时候,我们用它标示了-127。再减去1,就变成0000000了。还好我们有符号为,所以有两个0。把其中带符号的0拿过来,表示-128,刚好可以满足表示范围。
以上只是从软件的角度进行了分析,当然,从硬件的角度出发,负数使用补码表示也是有其原因的,毕竟计算机中,最终实现运算的还是硬件。主要原因有三:
1、负数的补码,与其对应正数的补码之间的转换可以用同一种方法----求补运算完成,简化硬件。
如:
原码反码补码
-127-〉12710000001-〉01111110-〉01111111
127-〉-12701111111-〉10000000-〉10000001
-128-〉12810000000-〉01111111-〉10000000
128-〉-12810000000-〉01111111-〉10000000
可以发现,负数和正数求补的方法是一样的。
2、可以将减法变为加法,省去了减法器。
在计算机中,我们可以看到,对其求补,得到的结果是其数值对应的负数。同样,负数也是如此。
运算中,减去一个数,等于加上它的相反数,这个小学就学过了。既然其补码就是其相反数,我们加上其补码不就可以了。
如:A-127,
也就相当于:A+(-127),
又因为负数是以补码的形式保存的,也就是负数的真值是补码,既然这样,当我们要减一个数时,直接把其补码拿过来,加一下,就OK了,我们也可以放心地跟减法说拜拜了!
当然这也涉及到类型转换的问题,如单字节128,其原码是10000000,其补码也是10000000。这样我们+128,或者-128,都是拿10000000过来相加,这样不混乱掉了?还好,各个编程语言的编辑器对有类型转换相关的限制。
如:(假设常量都是单字节)
1+128,真值的运算是00000001+10000000,如果你将结果赋值给一个单字节有符号正数,编辑器会提示你超出了表示范围。因为运算的两个数据是无符号的,其结果也是无符号的129,而有符号单字节变量最大可以表示的是127。
1-128,真知的运算是00000001+10000000,因为-128是有符号,其运算结果也是有符号,10000001,刚好是-127在计算机中的真值。
3、无符号及带符号的加法运算可以用同一电路完成。
有符号和无符号的加减,其实都是把它们的真值拿过来相加。真值,也就是一个数值在计算机中的二进制表示。正数的真值就是其原码,负数的真值是其补码。所以,有符号和无符号由编译器控制,计算机要做的不过是把两个真值拿过来相加。
第四天
优先级:
偏最高的:[]()
算数运算:!~++--
逻辑运算(&&||偏低,低于+-*/位运算)(逻辑比较小于关系比较)
赋值?=
最低:逗号运算
Switch(a)
{
Case1:
Case2:
Default:
}
以上中case在编译成汇编后不会生成语句,只相当于一个标号,所以每个case 后要跟break,否则后面的case将顺序值行。其中case为关键字,default不为关键字。Break使用后是跳出立它最近的一次循环。Return是退出最近的函数。
Rand()产生一个随机数。
Srand()选择随机墒池。
Time或得时间,至1970年起的秒数。
有符号数与无符号数之间运算问题
有符号数与无符号数之间运算问题
这个问题测试是否懂得C语言中的整数自动转换原则,有些开发者懂得极少这些东西。当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此,从这个意义上讲,无符号数的运算优先级要高于有符号数,这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
首先进行一个实验,分别定义一个signed int型数据和unsigned int型数据,然后进行大小比较:unsigned int a=20;
signed int b=-130;
a>b?还是b>a?实验证明b>a,也就是说-130>20,为什么会出现这样的结果呢?
这是因为在C语言操作中,如果遇到无符号数与有符号数之间的操作,编译器会自动转化为无符号数来进行处理,因此a=20,b=4294967166,这样比较下去当然b>a了。
第五天
虚拟地址空间图:
数组:
字符数组
非字符数组
数组个数计算:
Int a[10];
number=sizeof(a)/sizeof(a[0])
编译器不检查数组使用越界问题,如定义一个数组a[10],若对a[11]
对连续空间付值应严格控制越界问题。
代码溢出攻击:
数组名为一个只读地址
Sprintf格式化字符串赋值。
Memset(连续空间首地址,初值,长度),对内存初始化。
Localtime:
1、内存分配图:虚拟内存(4G)【VM】!=物理内存【PM】程序员只关心VM,操作系统帮
我们把VM转化为PM,转化盒子叫MMU
2、分界点:代码段(readonly存代码,常量)【0x08048000】---全局变量区---堆----栈(所有函数内部定义的局部变量都在这里)【前缀0xbf000000】
3、数组是分配连续空间的,而指针仅仅是指向连续空间的,它一定只占4个字节(32bit)。
char*p="hello world";char a[]={"hello world"};
sizeof(p)=4;sizeof(a)=12;p[n]=0x1[错误]修改字符串常量
4、如何读变量(先右后左)右:a[n](把变量a升级为连续空间的首地址,同时告诉这个变量a
具备n个单位,每个单位多少字节不知道,具体字节由变量的外边的数据类型决定)
5、什么是数据类型(整型,实型,结构体类型,自定义类型,指针,函数)在内存中具备大小
6、指针一定在声明的时候指定指向的类型(必须)。在使用的时候不用。
int a[10];a[9]
7、一维数组:一个[],典型的连续空间,空间的等分的。[n],n是可以很大。a[4],以a为内存空
间的地址,向下偏移4个单位。
8、[n],n是常量,不要使用变量,同时编译器和系统都不检查在数组使用时,是否越界,非常数据
异常,内存溢出。
9、const:只读,仅仅编译器和程序员。
10、数组的初始化:直接在声明的时候赋值{}。
11、字符型数组,非字符型数组:字符型:结束标志:0,while(a[i])。
非字符型:结束:统计个数:sizeof(a)/sizeof(a[0])。
12、字符处理的库函数:sprintf(char*,...)内存清理的库函数:memset(内存空间,赋值,长度【单位字节】)
所有的变量及数组定义后,均需初始化,不同的编译器在定义时给变量的初始值不同,只有部分为0,为了解决这种不可控性,所以必须初始化,包括内存的初始化。
对于有以下两种定义:
Char*p=”kingkong”;
Char a[]=”kingkong”;
若出现p[0]=’i’;将出现错误,而a[0]=’i’;不会。
第一句是定义了一个地址为“kingking”这一常量地址的指针,出现p[0]=’i’,的意思是对常量’k’进行修改,而常量是受系统保护的,不能修改。所以出现错误。而第二句话的意思是定义了一个数组,并对其进行初始化,两句话的根本区别是它们的“kingkong”在内存中的位置不同。
内存思想
程序员在编程时的虚拟地址,每一位对应的是1个字节,对于int变和其它大于一字字的变量,用连续的多个地址共同存储,对于高位是否在地址的高位则要根据系统的大小端模式不同而变化。而CPU 在运行处理的时后,是根据CPU数据总线长位决定多少字节,如在32位系统中0x01《《31位是可以行到正确结果的。
第六天
二维及多维数组在内存中的空间占用。
Char a[2][2]={{1,2},{3,4},{5,6}};
Char*p;p=&a;
则:p[1]=a[0][1]=1;
a[1]=a[1][0]=3;
b[3][4][5],b[1]=b[1][0][0];
多维数组在内存中按行转为一维后存储。
Char*a[];则a[0][1]表示以a[0]所存的地址为首地址偏移1位取值。
指针:
特点:
使和序简洁、紧凑、高效
表示复杂的数据结构
动态分配内存
反回不此一个的函数值。
Int*a[10];
Int*a[10];
Int(*a[10])[10];
二维指针并不是二维数组,
*p++由于‘*’和P处于同一优先级,所以*p++相当于*(p++)。
NULL表示指针未指向任何一个对像。也是c语言中唯一的指针常量。
1、二维数组的特点:代表一个面,行*列int a[3][4]3:行,4:列数为了程序的逻辑分析面(i,j)
2、三维数组:体积,n个4*3的面构成的体积:int a[n][4][3]
3、n维最终都要转换成一维数组存储在内存中
char buf[4][16];4行字符串‘\0’printf("%s\n",buf[3]);
int main(int argc,char*argv[])
{
printf("the argv[1]is%s\n",argv[1]);
}
4、指向数组名的地址:
int a[3];int a1[3][4];int a2[3][4][5];
*p??
p=a;
int*p;p=a;
int(*p1)[3];p1=&a
int(*p)[4];p=a1;
int(*p2)[3][4];p2=&a1;
int(*p)[4][5];p=a2;
int(*p3)[3][4][5];p3=&a2;
对于数组a,a是指其对应一行的值,&a是整个对像块。
4.main()
{
int a[5]={1,2,3,4,5};
int*ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
输出结果是什么?
答案:输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int*ptr=(int*)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为int(*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].