0.0C语言重点问题回顾
-
左值和右值得区别:左值是用来表明变量的身份的,右值更加侧重于值本身;
-
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;
- 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*));
}
先生成一个数组,每个数组元素都是一个指针,然后我们依次为每个指针分配一段内存空间。
- 函数指针:我们过去所接触的指针通常是指向变量,但是在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语言中内存都是相同的,如何解释他们,依据的是采用什么指针操控他们
- 程序从编写到运行:
a)预编译:主要处理了以#开头的指令,#include #ifndef #define
b)编译:编译针对的是单个源文件,生成.o目标文件
c)链接:将每个目标文件链接起来,生成可执行文件
头文件中不加预编译指令,造成重复定义是编译期错误;头文件中定义int a = 10;之类的语句属于链接期间错误。
8.解决互斥问题的方案是:
a)互斥锁、信号量
b)原子操作,gcc提供CAS
互斥是一种竞争关系,同步则是一种合作关系。有可能产生互斥问题的场景称为竞态条件。
-
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线程模型 -
大小端问题:TCP传输,对于字符串不需要考虑大小端,对于int等需要考虑。总之就是对于以字节为逻辑单位的数据不需要考虑大小端。