c语言点滴记录
在这里整理最近查看到的博客。一方面,整理并记录知识;另一方面,整合一下学习的基础知识,打牢基础。废话不说,开始整理:
原、反、补码的思考计算机的深入思考,为什么机器中计算都用的是补码而不是原码或是反码呢?原因有两个:补码表示的范围更广。针对于一个字节,可以表示从-128到127,(-128的补码是10000000),而原码和反码都有正负零的存在,表示的范围只能是-127到+127;补码更利于计算,并且可以简化电路设置(硬件只实现加法器就好了)。两个数相减,这要将减数转换成负数的补码,然后做位相加就好了。例如,1-128可以转化成0000 0001 +1000 0000=1000 0001,结果为-127。
函数返回一个二维数组
一个函数返回一个二维数组。首先,要向内存中申请一个相应长度的一维指针数组,数组中每个元素指向一个一维数组;然后,动态申请一个一维数组空间;最后,将一维数组的首地址值赋值给指针数组的一个元素。如果二维数组有n行,那么就要重复步骤2、3,直到将n行赋完值。在写代码之前,需要直到的是,二维数组在内存中是以一维形式存储的(可以写代码验证一下吆!)。具体代码如下:
//函数返回二维数组 char** getarray(int n/*row*/,int m/*collumn*/){ char** arr = (char**)malloc(sizeof(char*)*n); char *p=(char*)malloc(sizeof(char)*n*m);//一下子分配出所有元素 if(arr && p) { int i; for(i=0; i void main(){ int a[2][3]={1,2,3,4,5,6}; int *p = (int*)a; int i; for(i=0; i<6;i++)printf("%d->",*p++); printf("\nint* visited all 2dimension array.\n"); }
malloc分配内存深入理解
malloc(0)是不会产生错误的,这是这一小节的前提(如果你不服,可以上机检查一下!)。下面解释原因,malloc在分配空间的时候会额外分配一小部分空间(一般为8b),这一部分空间用来维护堆上的内存分配链表。
学习使用void*
指针一般包含两个属性,一个是标记内存分配的起始地址,另一个是告诉编译器向后寻找内存的步长。例如,int* p=malloc(sizeof(int)*n),p一方面告诉编译器分配内存的地址,另一方面告诉编译器访问的步长是4b。
void类型的指针,一种特殊的指针,只有内存的起始地址,而不知道访问内存的步长。由于void型指针的特殊性,这类指针在使用时要时刻记住进行指针类型的转换。
封装使用自己的类库
回调函数
回调函数的核心就是函数指针。函数指针作为另一个函数的参数,传递给这个函数后,这个函数可以使用这个指针访问其指向的函数,从而完成对该函数的调用。在这里需要明确函数指针的定义和使用。如何认识指针?把它当做一种数据类,或者更确切地说是看成一种特殊的指针。下面看一下代码。
#include /*funtion pointer typedef*/ typedef int (*pfun)(int,int); void use_fun_pointer(int n,pfun func){ printf("%d\n",n+func(2,3)); } int func(int a,int b){ return a+b; } void main(){ pfun f=&func; use_fun_pointer(-5,f); }
代码输出结果为0
上面的代码可以将函数指针理解成是一种特殊的类型,更是一种指针。理解了函数指针,那么c#中的代理机制应该就好理解一些了。
我们在学习数据结构的时候,接触到的是如下形式来构建链表的:
struct node{ int data; struct node *next,*pre; };
但是,linux内核中使用的链表实际上是这样的吆:
struct list_head { struct list_head *prev,*next; }; struct entry { struct list_head* list; type data; };
注:上面的代码,正好说明了为什么malloc(0)回分配8b空间了,占空间的是entry中的list指针,而list指针结构体中含有两个指针,那么这两个指针分别占4b。
看了上面的代码,大家有没有产生一个疑问,如果我知道entry的实例,我很容易范文data属性。然而,在linux内核中我们会只知道链接各个内存块的链接(即entry中的list属性)。那么,我们怎么才能只知道list的情况下找到data呢?以前没学过呀,我怎么知道呢?下面学习一下新知识。
#define list_entry(ptr, type, member) container_of(ptr, type, member) #define container_of(ptr, type, member) ({ / const typeof( ((type *)0)->member ) *__mptr = (ptr); / (type *)( (char *)__mptr - offsetof(type,member) );}) #define offsetof(type, member) ((size_t) &((type *)0)->member)
解释都不说了,因为比较复杂,也很绕。给出一个参考。
结构体的深浅拷贝只贴代码吧!代码来自!
typedef struct teacher { char name[20]; char *pname; int age; }teacher; void copystruct2(teacher *to,teacher* from) { // *to = *from;//编译器的==操作 只会把指针变量的值copy,但不会拷贝指针变量所指的内存空间,浅拷贝 memcpy(to,from,sizeof(teacher)); to->pname = (char *)malloc(20*sizeof(char));//深拷贝 strcpy(to->pname,from->pname); }野指针问题
产生野指针的原因如下:
(1)指针变量未初始化
任何指针变量刚被创建时不会自动成为null指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为null,要么让它指向合法的内存。
(2)指针释放后之后未置空
有时指针在free或delete后未赋值 null,便会使人以为是合法的。别看free和delete的名字(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为null,防止产生“野指针”。
(3)指针操作超越变量作用域
不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。示例程序如下:
class a { public: void func(void){ cout << “func of class a” << endl; } }; class b { public: a *p; void test(void) { a a; p = &a; // 注意 a 的生命期 ,只在这个函数test中,而不是整个class b } void test1() { p->func(); // p 是“野指针” } };
函数 test1 在执行语句 p->func()时,p 的值还是 a 的地址,对象 a 的内容已经被清除,所以 p 就成了“野指针” 。
const变量不能更改吗?
常识性知识,直接贴代码(c语言):
void getmem(const char *p) { //p =1;//编译通过 //p[1] = 0;//报错 左值指定const对象 指针所指向的内存空间不能被修改 } void getmem2( char const *p) { //p =1;//编译通过 //p[1] = 0;//报错 左值指定const对象 指针所指向的内存空间不能被修改 } void getmem3( char* const p) { //p =1;//编译不通过 const修饰p p的值不能被修改 p[1] = 0;//编译通过 ,p指向的内存空间可以被修改 } void getmem4(const char* const p) { //p =1;//编译不通过 const修饰p p的值不能被修改 //p[1] = 0;//报错 左值指定const对象 指针所指向的内存空间不能被修改 }
c++的世界里,是这样的:c++中的const是真正的常量,存放在const符号表中,只有当const常量为全局在其他文件中使用或使用&操作符取const的地址的时候才分破存储空间。
const int b =20; int *p = null; p =(int*)&b; *p =60; printf("b:%d\n",b);//20 system("pause"); return 0;
上一篇: C语言:猜数字游戏