C语言总结
# 内存四区模型
# 分区
- 栈区 由编译器自动分配和释放,存放函数的参数值,局部变量的值; 系统分配空间,系统自动回收,函数内部定义的变量,函数参数,函数结束,其内部变量生命周期结束
- 堆区 由程序员自动分配(动态内存申请与释放),若程序员不释放,没有手动释放,分配的空间一直可用,程序结束时可能由操作系统回收。
- 全局区 存放全局变量和静态变量,程序结束后由操作系统释放。
- 常量区 存放字符串常量,程序结束后由操作系统释放。
- 代码区 存放函数体的二进制代码。
# .c -> 可执行程序过程
- 预处理(gcc -E a.c -o a.i):宏定义展开、头文件展开、条件编译,这里并不会检查语法
- 编译(gcc -S a.i -o a.s):检查语法,将预处理后文件编译生成汇编文件
- 汇编(gcc -c a.s -o a.o):将汇编文件生成目标文件(二进制文件) (nm -C a.o进行查看)
- 链接(gcc a.o -o a -lc -lgcc, -lc 和-lgcc 是默认的,可以省略):将目标文件链接为可执行程序
# 指针
# 指针是一种数据类型
指针的步长(p++),根据指针所指内存空间的数据类型来确定。
用n级指针形参,去间接修改n-1级指针的值。
# 理解指针必须和内存四区相结合
- 主调函数 被调函数
- 主调函数可把堆区、栈区、全局数据内存地址传给被调用函数
- 被调函数只能返回堆区、全局数据
- 指针做函数参数,是有输入和输出特性的
- 指针做输入:主调函数分配内存
- 指针做输出:被调用函数分配内存
# 二级指针
⼆级指针作为形参,可以间接的修改实参的值
# 二级指针的三种输入模型
- 第一种输入模型
char *myArray[] = {"aaaaaa", "ccccc", "bbbbbb", "111111"};
void printMyArray(char **myArray, int num);
void sortMyArray(char **myArray, int num);
- 第二种输入模型
char myArray[10][30] = {"aaaaaa", "ccccc", "bbbbbbb", "1111111111111"};
void printMyArray(char myArray[][30], int num);
void sortMyArray(char myArray[][30], int num);
- 第三种输入模型
char **myArray = NULL;
char **getMem(int num);
void printMyArray(char **myArray, int num);
void sortMyArray(char **myArray, int num);
void arrayFree(char **myArray, int num);
/* 观点:二级指针做输入的三种内存模型是以编译器分配内存地点的不同分类的。 */
// 第一种内存模型:编译器分配在常量区上的内存。
Char *myarray[]={"fdskajlfjie","fsdlajfie","fkdsaljfie","fdksajfi"};
// 定义一个指针数组,数组中每个元素是指针,指向一串字符串(该字符串分配在字符串常量区)。
// 排序时,交换的是指针的指向,并没有使用*p .
#include <stdio.h>
void display(char **p, int num) {
int i = 0;
for (i = 0; i < num; i++)
printf("%s ", p[i]);
}
void sort(char **buf, int num) {
int i = 0, j = 0;
char *temp = NULL;
for (i = 0; i < num; i++)
for (j = i; j < num; j++){
if (strcmp(buf[i], buf[j]) > 0){
temp = buf[i];
buf[i] = buf[j];
buf[j] = temp;
}
}
}
}
void main()
{
char *buf[] = { "aaaaa","bbbbb","ffffff","ddddd","ccccc","22222" };
int i = 0, j = 0;
int num = 0;
char *temp = NULL;
num = sizeof(buf) / sizeof(buf[0]);
display(&buf, num);
sort(&buf, num);
printf("\n");
display(&buf, num);
system("pause");
}
// 第二种内存模型://编译器在栈上分配的内存。
//排序时交换的是内存块,不是指针。
//数据存放在数组中,编译器分配内存在临时区(栈区)
//使用数组进行存储。
//strcmp的用法:比较两个字符串的大小。strcmp(str1,str2)>0;:表示的是如果str1的字符比str2大的话返回比一大的值。否则反之。
#include"stdio.h"
void sort(char p[][30], int length) {
int i, j;
char tem[10];
for (i = 0; i < length; i++){
for (j = i; j < length; j++){
if (strcmp(p[i], p[j]) > 0){
strcpy(tem, p[i]); //对数据进行排序
strcpy(p[i], p[j]);
strcpy(p[j], tem);
}
}
}
}
void display(char buf[][30],int length) {
int i;
for (i = 0; i < length; i++)
printf("%s ", buf[i]); //打印数据
printf("\n");
}
void main()
{
char buf[10][30] = { "11111","aaaaaa","cccccc","dddddd","bbbbbbb" };
int i = 0, j = 0;
int num = sizeof(buf) / sizeof(buf[0]);
printf("%d", num);
display(buf, num);
sort(buf, num);
display(buf, num);
system("pause");
}
// 第三种模型:程序员自己在堆上分配的内存。
// 自己打造的内存空间,首先定义一个二级指针变量,再用malloc函数分配一组存放一级指针的内存把地址赋给二级指针,再用malloc函数分配内存存放内容。
// sprintf(内存首地址,”数据类型“,数据):将数据打印到内存中.
// Eg :sprintf(p,"%d",343); 将数据343以%d的形式输入到p所指向的内存空间。
//可以用指针交换排序,也可以交换指针所执行的内存空间。
void init3(char ***p, int num) //初始化方法一
{
char ** first = NULL;
int i;
first = (char **)malloc(sizeof(char*)*num); //分配内存存放一级指针
for (i = 0; i < num; i++){
first[i] = (char*)malloc(sizeof(char) * 100); //为一级指针分配空间存放数据
sprintf(first[i], "%c%c%c", 'a' + i, 'a' + i, 'a' + i);
}
*p = first;
}
void display3(char **p, int num) {
int i, j;
for (i = 0; i < num; i++) //打印
{
printf("%s\n", p[i]);
}
}
void sort3(char **p, int num)
{
int i, j;
char *tem = NULL;
for (i = 0; i < num; i++)
for (j = i + 1; j<num; j++) //排序
{
if (strcmp(p[i], p[j]) < 0){
tem = p[i];
p[i] = p[j];
p[j] = tem;
}
}
}
}
void main3() {
char **first = NULL;
int num = 5;
init3(&first, num);
display3(first, num);
sort3(first, num);
display3(first, num);
system("pause");
}
# 函数指针(指向函数的指针)
- 先定义函数类型,根据类型定义指针变量(不常用)
//有typedef是类型,没有事变量
typedef int FUN(int a); //FUN函数类型
FUN *p1 = NULL; //函数指针变量
//p1 = &fun;
p1 = fun; //p1 指向 fun函数
fun(5); //传统调用
p1(6); //函数指针变量调用方式
- 先定义函数指针类型,根据类型定义指针变量(常用)
typedef int(*PFUN)(int a); //PFUN, 函数指针类型
PFUN p2 = fun; //p2 指向 fun
p2(7);
- 直接定义函数指针(常用)
int(*p3)(int a) = fun;
- 函数指针数组的应用
//函数指针数组
void(*fun[5])() = { add, minus, multi, divide, myexit };
//指针数组
char *buf[] = { "add", "min", "mul", "div", "exit" };
char cmd[100];
int i = 0;
while (1)
{
printf("请输入指令:");
scanf("%s", cmd);
for (i = 0; i < 5; i++)
{
if (strcmp(cmd, buf[i]) == 0)
{
fun[i]();
break; //跳出for()
}
}
}
- 回调函数 把指针作为参数传递到其他函数,指向函数的指针也可以作为参数,以实现函数地址的传递。
# 动态库
windows动态库生成的文件有 socketClient.lib socketClient.dll windows 动态库是dll文件和lib文件组合。
xxx.lib:编译代码时,需要链接此文件 xxx.dll:运行程序时,需要链接此文件
生成 DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将 __declspec(dllexport) 添加到头文件中的声明。
xxx.c 文件中实现函数的定义,需要在函数的前面添加: __declspec(dllexport)
#ifdef DLL_EXPORTS
#define SIMPLE_CLASS_EXPORT __declspec(dllexport)
#else
#define SIMPLE_CLASS_EXPORT __declspec(dllimport)
#endif
# 补充
- c语言中的const
C语言中const是一个冒牌货,通过强制类型转换,将地址赋给变量,再作修改即可以改变const常量值。
#include <stdio.h>
int main()
{
const int a = 10;
printf("%d ", a); // 10
int* p = &a;
*p = 20;
printf("%d ", a); // 20, 此时常量的值被修改
}
- 无符号与有符号数的区别
- 在默认情况下声明的整型变量都是有符号的类型(char有点特别),如果需要声明无符号类型的话就需要在类型前加上unsigned。
- 无符号整型和有符号整型的区别就是无符号类型可以存放的正数范围比有符号整型中的范围大一倍,因为有符号类型将最高位存储符号,而无符号类型全都存储数字。比如16位系统中一个int能存储的数据的范围是-32768~32767,而无符号能存储的数据则是0~65535。
- 在一些不可能取值为负数的时候,可以定义为unsigned,在一些底层的嵌入式编程的数据一般都是无符号的。
- 枚举
枚举类型的定义形式:
enum typeName{ valueName1, valueName2, valueName3, ...... };
枚举的取值默认从0开始,注意最后的;不能少。
c语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而c++中枚举变量,只能用被枚举出来的元素初始化。(真正的枚举)
# 小技巧
- 分配内存后,通过memset设置内存中的值 memset(buf,0,sizeof(Teacher)*num);