C学习笔记


定义头文件时必须加:
#ifndef AAA_H
//所需要的头文件、宏定义、变量定义
//其中AAA为.c文件的文件名
#endif

 

int fun(int *p);对于这样的函数,如果提示要让p自身修改值,那么传递给p的必须是一个有空间的变量或指针,一般是int pp;则有
   fun(&pp);
如果是对内存操作,则一般是
int fun(int **p)
{
*p=(int *)malloc(size);
return 0;
}
 或都是
int *fun()
{
return (int *)malloc(size);
}
 
void *fun()
{
retrun (void *)malloc(size);
}  

       

open打开错误时,并不会终止程序,所以后面往往有exit这样的语句

定义字符数组时必须要考虑最后一个元素,最后一个元素必须为'\0',它的数值为0
但是其它类型的数据无须这样考虑

为0(stdin,标准输入)、1(stdout,标准输出)、2(stderr,标准错误输出),默认与keyboard、monitor有关;

对于int a[2][3]={1,2,3,4,5,6};
a代表二维数组第一行的地址,是行首的地址,它是一个行常量指针,而a[0]相当于a[0]+0,指向第一行第一列元素的地址,相当于*(a+0),可以直接替换,即第一行首地址a中存放了第一行元素的首地址,而a[0]存放第一行第一列的首地址
a=*a,但它们的含义不同,a+1为第二行的首地址,而*a+1为第一行第二列的首地址
即有a是指向行的行常量指针,而*a是指向列的常量指针,并且并不存在*a这样一个具体的存储单元,这是一种计算方法,我们不能这样定义一个:int **p;p=a;这是错误的,因为a当中的元素不是地址
如果有定义:int (*p)[3],则可以有:p=a;即p指向第一行首地地,指向行,p[0]为第一行第一列,p[0][0]为第一行第一列的值,数组名a其实是一个常量行指针,不能自加自减,p是一个指向拥有三个元素的数组,(*p)[0]=p[0][0],可以表示为*(*(p+i)+j),即p[i][j]
*(p[0]+1)为2,(*p+1)[1]为3,(*(p+1))[1]为5
a不是指向指针的指针

char *a[]={"111","2222"};//多维字符串的定义方法,但二维数组必须指定列的长度,这里a是一个指针数组,元素是地址
即a[0]="111";是一个地址

char **p;//指向指针的指针,含义就是:p里面存入的是指向char型指针(地址)的指针(地址)
此时可以有:p=a;即有*p=a[0],还是一个地址,此时会有p与*p会不同的值,即它们指向不同的地址,*p具有真实内存空间,不同于二维数组名当中的*a,二维数组名的*a不具有真实的空间,**p为字符1如果以char型输出,则为字符1,如果以int型输出,输出的则为字符1的ASCII码

int a=100;
int *p;
int **pp;
p=&a;
pp=&p;
可以表述为p的值存放了a的地址值&a,所以p指向变量a所在的内存空间
又因为pp的值存放了p的地址值&p,所以pp指向变量p所在的内存空间
*p为变量a的值,因为p直接指向a所在的空间,所以修改*p就是修改a,但是前提是p必须是指向a的内存空间,修改p值后则不行,因为
造成了p所指向的内存空间的改变
同理,*pp为p的值,修改*pp则修改了p的指向,不能修改pp本身,修改本身同样是修改了指向,会造成pp所指向的内存空间改变


一个函数中,如果有指针参数,且有关于指针参数的长度限制,那么指针参数必须是一个已经定义了的空间,如用malloc或全局变量空间地址,这个函数本身不再开辟空间,eg: int fun(char *p,int p_len);
反过来如果一个函数中仅用了指针,没有关于指针的长度参数,那么指针值必然可以用这个局函数获得,这个局部函数中可能有malloc等函数,eg: int fun(char *p);但如果是int fun(const char *p);表明不会修改p指向的内容,那么一般p也是个已经定义的空间首地址

 

volatile变量的几个例子 
 推荐一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:   1). 并行设备的硬件寄存器(如:状态寄存器)  
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables:静态变量与全局变量)
3). 多线程应用中被几个任务共享的变量
注意:在由一个mutex限定的临界区里,只有一个线程可以进入。因此,在临界区中执行的代码有和单线程程序有相同的语义。被控制的变量不会再被意外改变——你可以去掉volatile修饰,当然这只针对于第2,3个条件。


char *p;//这里只是定义了一个p用于存放某个字符或者字符串变量的地址,但现在还未初使化,它没有获得某个变量的空间首地址
int *p;//这里只是定义了一个p用于存放某个int或者int数组的地址,但现在还未初使化,它没有获得某个变量的空间首地址

static int a=0;//静态变量必须开始就初使化
对于类中的静态变量,必须在完成定义类之后进行初使化,它的初使化不是在main()函数中,而是在类外
<数据类型> <类名> ::<静态数据成员名>=<值>

用"int x = rand()%100;"来生成 0 到 100 之间的随机数这种方法是不或取的,比较好的做法是: j=(int)(n*rand()/(RAND_MAX+1.0))产生一个0到n之间的随机数

函数默认是extern型,变量默认是static型,一个文件中定义的全局变量要在另一个文件中使用,即一个中定义int a或int a[10];
那么另一个文件要用extern int a或extern int a[];
对于函数,只要include包含另外一个文件中定义的函数的声明即可,extern关键字可以省略

分内分配:
一个由C/C++编译的程序占用的内存分为以下几个部分

  1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
       堆(heap):由malloc,new等分配的空间的地址,地址由低向高增长(程序员释放)。

  2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
       栈(stack):是自动分配变量,以及函数调用所使用的一些空间(所谓的局部变量),地址由高向低减少;

  3、全局区(静态区)(static)— 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。-->分别是data区,bbs区 

  4、文字常量区 — 常量字符串就是放在这里的,程序结束后由系统释放 。-->coment区

  5、程序代码区 — 存放函数体的二进制代码。-->code区
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0;// 全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[] = "abc"; //栈
char *p2; 栈
char *p3 = "123456"; //123456\0在常量区,p3在栈上。
static int c =0;// 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配 得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}


指向函数的指针:
int fun(int i)
{
return i;
}
int (*p)(int);
那么对于fun的调用可以有两种方法:1、p=fun;(*p)(4);或者2、p=fun;p(4);
即p(4)与(*p)(4)等效

 

关于可重入代码
简单介绍,因为驱动可以被多个进程调用,互不干扰,这样驱动必须是可重入的。
可重入最简单的理解就是所有变量都是局部变量。

大小端针对的是CPU对一个数据的存放方式的不同,对数组没有影响,如int a[2]={0x1234,0x5678},小端存储,则a[0]的低字节0x34在CPU
中放在0x12的后面,而a[1]仍然在a[0]的前面,其中高字节0x56放在0x78的前面,0x34后面存放0x56
小端存储(Little Endian)。字节或半字节的最低位字节(Lowest Significant Bit,LSB)存放于内存最低位字节地址上。即最低地址存放的最低字节,一个用十六机制表示32位数据12345678H,存放在存储字长是32为的存储单元中。整个存储字读出的结果就是12345678H,于软件设计者的书写习惯相符,为Power PC,Intel x86 、ARM大部分系列等采用。

大端存储:低地址存放高字节数据,高地址存放低字节数据,如十六进制0x1122,那么,0x11则存放在低地址,0x22高地址,最后则
为0x22 0x11,假设把它放在array[2]中,则array[0]为0x11,array[1]为0x22

当对字符串采用逐个赋值的时候,必须要在末尾赋值'\0',否则可能出现乱码,采用strcpy会自动赋值'\0'


void *fun(void *)
{
return (void *)0;//这个函数仍然有返回值,因为它是一个指针函数,返回值是一个指针,这个指针指向一个void类型的数
}

int fun( int **p)
{
*p=malloc(SIZE);//不能用存储在栈区的变量,否则p值会被释放,malloc与new开辟的空间位于栈区,可以通过p传递到其它地方
return 0;
}
此时调用:int *pp;fun(&pp);pp就可以获得开辟的空间,但是int fun(int *p)不行,它只能让调用它的函数改变*p的值,在内存空间中

sprintf 是个变参函数,定义如下:  
int sprintf( char *buffer, const char *format [, argument] ... );  
 除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:   格式化字符串上。   printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。 
  如:   
//把整数123 打印成一个字符串保存在s 中。   
sprintf(s, "%d", 123); //产生"123"   
可以指定宽度,不足的左边补空格:   
sprintf(s, "%4d %d",123, 4567); //产生:" 123 4567"   当然也可以左对齐:

int *p=(int *)malloc(size);p仍然是一个局部变量,只不过它指向一个堆空间

返回值为引用的函数:其返回值应该为全局或者堆空间的变量,因为引用就是另一个变量的别名,因此返回引用就不会另外产生临时变量,由返回的值(右值)直接赋值给左值,因此右值不能是局部变量。左值不会受右值影响,但左值有可能影响右值。只有当要对自身操作时,一般才会返回引用。返回引用时右值直接指向返回的变量,因此返回的变量不能是局部变量。右值为引用并不会影响左值,只是函数在返回过程中,如果引用返回的变量是局部的,那么值无法正常返回给左值。

函数之前加&并不是地址,单独的函数名即为地址

左值引用是直接指向右值的空间,相当于右值的别名。当右值是const类型时,左值引用也必须是const类型     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值