欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c语言点滴记录

程序员文章站 2023-02-02 09:12:37
在这里整理最近查看到的博客。一方面,整理并记录知识;另一方面,整合一下学习的基础知识,打牢基础。废话不说,开始整理: 原、反、补码的思考 计算机的深入思考,为什么机器中计算都用的是补码而不是原码或是...

在这里整理最近查看到的博客。一方面,整理并记录知识;另一方面,整合一下学习的基础知识,打牢基础。废话不说,开始整理:

原、反、补码的思考

计算机的深入思考,为什么机器中计算都用的是补码而不是原码或是反码呢?原因有两个:补码表示的范围更广。针对于一个字节,可以表示从-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#中的代理机制应该就好理解一些了。

linux中内核中使用链表记录空间

我们在学习数据结构的时候,接触到的是如下形式来构建链表的:

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;