本文主要记录c语言编码过程中的一些注意事项。
1 C语言编码注意事项
1.1 申请的动态内存一定要做初始化工作
例如:如下所示的代码,若未对table->bucket指针数组赋初值,该数组中就有可能存在非法值,导致代码访问到非法内存。
1
2
3
4
5
6
7
8
9
10
11
12
|
int initHashTable(HashTable* table, int bucket_num)
{
table->bucket_num = bucket_num;
table->bucket = (HashNode**)malloc(sizeof(HashNode*) * bucket_num);
if (table->bucket == NULL) {
printf("Alloc memory failed.\n");
return -1;
}
// 此处一定要做初始化工作,否则可能存在非法内存访问的情况
memset(table->bucket, 0, sizeof(HashNode*) * bucket_num);
return 0;
}
|
1.2 对结构体变量初始化时,需要初始化所有的变量
实例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
struct ListNode {
int val;
struct ListNode *next;
};
ListNode* initNode(int val)
{
ListNode* node = (ListNode*)malloc(sizeof(ListNode));
if (node == NULL) {
return NULL;
}
node->val = val;
// 注意此处一定要初始化,否则有可能访问到非法内存
node->next = NULL;
}
|
1.3 c语言初始化数组的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
//Unless that value is 0 (in which case you can omit some part of the initializer and
//the corresponding elements will be initialized to 0), there's no easy way.
//Don't overlook the obvious solution, though:
int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };
//Elements with missing values will be initialized to 0:
// initialize to 1,2,0,0,0...
int myArray[10] = { 1, 2 };
//So this will initialize all elements to 0:
int myArray[10] = { 0 }; // all elements 0
//In C++, an empty initialization list will also initialize every element to 0. This is not allowed with C:
int myArray[10] = {}; // all elements 0 in C++
//Remember that objects with static storage duration will initialize to 0 if no initializer is specified:
static int myArray[10]; // all elements 0
|
1.4 字符数组初始化的方法
1
2
3
4
5
6
7
8
9
|
//If you don't want to change the strings, then you could simply do
const char *a[2];
a[0] = "blah";
a[1] = "hmm";
//If you do want to be able to change the actual string content, the you have to do something like
char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");
|
1.5 16进制字符串转换为整数字符串
1
2
3
4
5
|
char str[] = "0x1800785";
int num;
sscanf(str, "%x", &num);
printf("0x%x %i\n", num, num);
|
1.6 typedef用法
1
2
3
4
5
6
7
|
// typedef existing_name alias_name;
typedef unsigned int UINT;
typedef struct Node {
int val;
struct Node* next;
} Node;
|
2 C语言中和指针相关知识
2.1 与指针有关的函数声明
1
2
3
4
5
6
7
8
|
//声明返回指针数组的函数
int (*func(int i))[10];
//声明一个指向函数的指针(pf)
bool (*pf)(const string &, const string &);
//声明一个名字为pf的函数,该函数返回bool*
bool *pf(cosnt string&, const string &);
//声明一个返回函数指针的函数
int (*f1(int))(int *, int);
|
2.2 与指针有关的数组声明
指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针
数组指针:a pointer to an array,即指向数组的指针
例如:
int* a[4] 指针数组
表示:数组a中的元素都为int型指针
元素表示: *a[i]和*(a[i])是一样的,因为[]优先级高于*
int (*a)[4] 数组指针
表示:指向数组a的指针
元素表示:(*a)[i]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
int main(){
int c[4]={1,2,3,4};
int *a[4]; //指针数组
int (*b)[4]; //数组指针
b=&c;
//将数组c中元素赋给数组a
for(int i=0;i<4;i++){
a[i]=&c[i];
}
//输出看下结果
cout<<*a[1]<<endl; //输出2就对
cout<<(*b)[2]<<endl; //输出3就对
return 0;
}
|
2.3 数组名和指针的区别
2.3.1 数组名不是指针
验证程序:
1
2
3
|
int data[10];
cout << sizeof(data) << endl;
cout << data << &data << endl;
|
说明:第一行的输出结果为40,说明data不是一个指针,编译器将data当作一个数组来处理。此处data为包含10个整型元素的数组。
2.3.2 数组名作为常量指针
数组名可以转换为指向其指代实体的指针,而且是一个指针常量。该指针指向数组的第一个元素。
验证程序:
1
2
3
4
5
6
|
char str1[10] = "I Love U";
str1++; //该语句编译时会出错,因为str1是常量
strcpy(str2, str1); //标记2
cout << "string array 1: " << str1 << endl;
cout << "string array 2: " << str2 << endl;
|
说明:标准C库函数strcpy的函数原形中能接纳的两个参数都为char型指针,而我们在调用中传给它的却是两个数组名。
2.3.3 指向数组的指针
指向数组的指针则是另外一种变量类型(在32平台下,长度为4),仅仅意味着数组的存放地址。
验证程序:
1
2
3
|
char str1[10] = "I Love U";
char *pStr = str; //标记1
cout << sizeof(pStr) << endl;
|
2.3.4 数组名当作函数实参
数组名在当作实参传入函数内部时,失去数组名的特性,成为普通的指针。
1
2
3
4
5
6
7
8
9
10
|
#include <iostream>
void arrayTest(char str[]){
cout << sizeof(str) << endl;
}
int main() {
char str1[10] = "I Love U";
arrayTest(str1);
return 0;
}
|
说明:程序的输出结果为4。
(1) 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;
(2) 很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
3 glibc源码阅读
3.1 实用gdb阅读glibc接口的方法
- 将glibc源码下载到本地,并解压。
- 在gdb中添加glibc源码路径;
- 方法1:在
.gdbinit文件中添加命令directory glibc_src_path
- 方法2:gdb调试二进制时,增加-d参数:
-d directory
3.2 理解glibc的简单方法:使用musl libc库
musl libc库实现了和glibc一样的功能,但是代码更简洁易懂。
可以通过阅读这个代码理解glibc中接口是如何实现的。
musl libc库网址