0.0C语言重点问题回顾

  1. 左值和右值得区别:左值是用来表明变量的身份的,右值更加侧重于值本身;

  2. void*是个例外,它只有基地址没有类型信息,所以无法解引用。

 int *p = malloc(100);
char *s = malloc(100);

很显然,p和s本身的值就是内存基地址的数值,但是p[3]和s[3]值是不相同的,因为p和s的类型不相同,p是int型指针,所以p[3]是把后面这段内存当做int数组,s[3]则是看做char数组,所以p++一次增加数值为4(32bit),而s++增加1;

  1. C语言参数传递的方式为值拷贝。函数形参拷贝实参的值后,与实参再无关联。正确的方式是采用指针,交换T类型的变量,那么swap函数的形参就要用T*类型的参数也就是说,交换两个int*,就要使用int**才可以达到目的。

4.关于数组传参:

a)一维数组用int做参数。在数组退化成int的过程中,数组仍可以正常使用,但是丢失了长度信息。

b)二维数组int a[4][3],采用int()[3]作为参数,int()[3]作为参数,int(*)[3]是一个数组指针。这里丢失了第一维,所以需要手工指定第一维的长度。

二维数组传参问题:一位数组可以看做是指针int*,二维数组也是指针但是不是int**而是一维数组的指针。

 void print_array(int (*a)[3], int m)
{
int i,j;
for(i=0; i !=m; ++i)
{
for(j=0; j !=3; ++j)
printf("%d", a[i][j]);
}
printf("\n");
}

这种做法的缺陷就是只能传递第二位为3的数组,要使其更加通用,可以用如下解决方案:

a.把数组用一个结构体包装起来,传参数的时候传递它的指针。

b.数组用动态内存去分配。

使用数组做参数,必须丢失最外围的长度信息,传递int a[10],丢失长度10,如果是int a[10][4],那么只能保留4,如果是int a[3][5][6],必须使用参数int ()[5][8],丢失最外面的3.

二维数组 int a[10][5] a[i]等价于
(a+i),而a[i][j]等价于((a+i)+j);

二维动态数组的构造方法:

 int **a = (int**)malloc(5 * sizeof(int*));
int i;
for(i=0; i != 5; ++i)
{
a[i] = (int*)malloc(4 * sizeof(int*));
}

先生成一个数组,每个数组元素都是一个指针,然后我们依次为每个指针分配一段内存空间。

  1. 函数指针:我们过去所接触的指针通常是指向变量,但是在C语言中函数也可以是具有指针的。
 int *p = &i;

这里仅仅是不能通过p修改i,例如*p = 2是错误的,但是i的值可以任意修改的,i = 34是正确的。

这里做了两个工作:

a.声明了一个int类型的指针,这个指针指向任何类型的int类型变量。

b.用i的地址去初始化p(这里是初始化而不是赋值),这样p就指向了i这个变量。

函数指针也是一样:

 void(* pfunc)(int, int);

这里我们声明了一个变量pfunc,它的类型是void(*)(int, int)。只需要pfunc = &test就能够让指针指向一个函数。这里指针函数看起来比较繁琐,我们可以尝试用typedef进行简化:

 typedef void(* func)(int, int);

需要注意的是:func是个类型不是变量。

5. C语言内存本身没有属性:没有所谓的int、char、float等,指针的本质:一个是内存的基地址,一个是变量的类型。

 int *p1 = malloc(1000);
char *p2 = malloc(1000);

这两段做的内存工作完全是一样的,都是向操作系统申请一块大小为1000的内存空间。并不存在说第一块内存是int类型,第二块是char类型。那为什么p1++和p2++指针变动的数值会不一样?原因是指针的类型不一样,这与他们只想的内存没有任何关系,所以可得结论:C语言中内存都是相同的,如何解释他们,依据的是采用什么指针操控他们

  1. 程序从编写到运行:

    a)预编译:主要处理了以#开头的指令,#include #ifndef #define

    b)编译:编译针对的是单个源文件,生成.o目标文件

    c)链接:将每个目标文件链接起来,生成可执行文件

    头文件中不加预编译指令,造成重复定义是编译期错误;头文件中定义int a = 10;之类的语句属于链接期间错误。

8.解决互斥问题的方案是:

a)互斥锁、信号量

b)原子操作,gcc提供CAS

互斥是一种竞争关系,同步则是一种合作关系。有可能产生互斥问题的场景称为竞态条件。

  1. Linux中创建进程的方式有:fork、Vfork、clone

    fork:采用了一种“写时复制(COW)”技术,传统的UNIX进程模型中,fork进程是把整个进程的项赋值一份,开销巨大。COW就是在以前的基础上,子进程仅仅复制父进程的页表,然后设置为只读,任何一方试图修改项就将该项单独复制一份。

    clone用于创建线程。在linux中线程是采用了一种轻量级进程的实现,所以linux中的线程有两个ID,pthread_self()的pthread_t类型是所谓的线程id,然而还有一个真实的进程id,类型pid_t,获取方式为gettid().我们采用的线程模型叫做NPTL,它采用的是1:1线程模型

  2. 大小端问题:TCP传输,对于字符串不需要考虑大小端,对于int等需要考虑。总之就是对于以字节为逻辑单位的数据不需要考虑大小端。

上一篇:Static and Instance Methods in JavaScript


下一篇:C#中 Reference Equals, == , Equals的区别