int a = 10;
int * p = &a;
指针
定义指针的三步骤
1 *与符号结合代表是一个指针变量
2 要保存谁的地址,将他的定义形式放在此处
3 用*p替换掉定义的变量
分析
1 与*结合代表这个一个指针变量
2 p是变量,p的类型是将变量p本身拖黑,剩下的类型就是指针变量的类型 int *。
3 指针变量p用来保存什么类型数据的地址,将指针变量p和指针变量p最近的*一起拖黑。剩下什么类型就保存什么类型数据的地址,int。
通过*取指针变量所指向那块空间内容时,取的内存的宽度和指针变量本身的类型有关
万能指针
不可以定义 void 类型的变量,因为编译器不知道给变量分配多大的空间,但是可以定义 void *类型,因为指针都是8个字节的地址,万能指针可以保存任意的地址。
const 修饰指针
const 修饰的是p,还是*
const int *p= &a;//const修饰的是*
不能通过 *p,改p所指向空间的内容,例如:*p=100:err 因为不能通过p改p所指向空间的内容
int *const p=&a;//const修饰的是变量p
p保存的地址不可以修改,例如:p=&b;err p本身的值不能被更改
const int *const p=&a;
p本身的指向不能改变,不能通过*p修改p指向向那块空间的内容
int a = 10;
int * p = &a;
int ** q = &p;
如果*和&相遇,相抵消
**q == *(*q) == *(p) == a;
**q == *(*q) == *(&a) == a;
[ ]并不是数组专属
[ ] == *()
p[0] == *(p+0);
指针数组
指针数组是一个数组,数组的每一个元素都是一个指针
main(int a,char *v[ ])
字符串处理函数
strcpy
将源字符串 src
的内容复制到目标字符串 dest
中,直到遇到空字符 \0
。
char dest[20];
char src[] = "Hello";
strcpy(dest, src);
strncpy
将源字符串 src
的前 n
个字符复制到目标字符串 dest
中,或者直到遇到空字符 \0
,取决于哪个条件先达成
char dest[20];
char src[] = "Hello";
strncpy(dest, src, 3); // 复制 "Hel" 到 dest
dest[3] = '\0'; // 手动添加空字符,以确保字符串终止
strcat
将源字符串 src
的内容连接到目标字符串 dest
的末尾,目标字符串 dest
必须足够大,以容纳两个字符串的总长度。
char dest[20] = "Hello";
char src[] = " World";
strcat(dest, src); // 连接 " World" 到 "Hello" 后面
strncat
将源字符串 src
的前 n
个字符连接到目标字符串 dest
的末尾,或者直到遇到空字符 \0
,取决于哪个条件先达成。
char dest[20] = "Hello";
char src[] = " World";
strncat(dest, src, 3); // 连接 " Wo" 到 "Hello" 后面
dest[8] = '\0'; // 手动添加空字符,以确保字符串终止
strcmp
比较字符串 str1
和 str2
的内容,如果相等返回0,如果 str1
小于 str2
返回负数,如果 str1
大于 str2
返回正数。
char str1[] = "apple";
char str2[] = "banana";
int result = strcmp(str1, str2); // 比较 "apple" 和 "banana"
strncmp
比较字符串 str1
和 str2
的前 n
个字符,如果相等返回0,如果 str1
小于 str2
返回负数,如果 str1
大于 str2
返回正数。
char str1[] = "apple";
char str2[] = "applesauce";
int result = strncmp(str1, str2, 5); // 比较 "apple" 和 "apple" 的前5个字符
sprintf
将格式化的数据写入字符串 str
。
char str[50];
int num = 10;
sprintf(str, "The number is %d", num); // 将 "The number is 10" 写入 str
sscanf
从字符串 str
中读取格式化输入。
char str[] = "123 4.56";
int num;
float fnum;
sscanf(str, "%d %f", &num, &fnum); // 从 str 中读取整数和浮点数
strchr
在字符串 str
中查找字符 c
的第一次出现,并返回指向该字符的指针。
char str[] = "Hello";
char *ptr = strchr(str, 'l'); // 查找第一个 'l'
strstr
在字符串 haystack
中查找子字符串 needle
的第一次出现,并返回指向该子字符串的指针。
char haystack[] = "Hello world";
char needle[] = "world";
char *ptr = strstr(haystack, needle); // 查找 "world"
strtok
分割字符串 str
为一系列子字符串,使用分隔符 delim
。
char str[] = "Hello,World,How,Are,You";
char *token = strtok(str, ","); // 使用逗号作为分隔符
while (token != NULL) {
printf("%s\n", token); // 逐个打印分割出的子字符串
token = strtok(NULL, ",");
}
atoi
将字符串 str
转换为整数。
char str[] = "12345";
int num = atoi(str); // 将 "12345" 转换为整数 12345
atof
将字符串 str
转换为浮点数。
char str[] = "3.14";
float num = atof(str); // 将 "3.14" 转换为浮点数 3.14
当不知道如何使用,使用man指令查看即可
man sscanf
内存分布
1局部变量
a >作用域在定义变量的{ }之内有效
b >生命周期程序运行至变量定义处开辟空间,所在的函数结束之后释放空间
c >未初始化的值随机
2静态局部变量
a >作用域在定义变量的{ }之内有效
b >生命周期执行 main 函数之前就已经开辟空间,程序结束之后才释放空间
c >未初始化的值0
3全局变量
a >作用域整个工程,所有文件
b >生命周期执行 main 函数之前就已经开辟空间,程序结束之后才释放空间
c >未初始化的值0
4静态全局变量
a >作用域当前文件
b >生命周期执行 main 函数之前就已经开辟空间,程序结束之后才释放空间
c >未初始化的值0
#include "stdio.h"
int a;
static int b;
int c = 10;
static int d = 10;
int main()
{
int e;
int f = 10;
static int g;
static int h = 10;
char * i = "test";
char * p = malloc(1024);
memset(p,0,1024);
strcpy(p,i);
printf("%s\n",p);
free(p);
return 0;
}
误区:这样是错误的,因为开辟空间的地址并没有给到main栈的p。在 fun
函数中,虽然分配了内存,但是没有将分配的内存地址返回给 main
函数中的 p
,因此 main
函数中的 p
仍然是 NULL
,导致后续的内存操作出现错误。
字符串常量存文字常量区在使用时,取的是字符串首元素的地址,文字常量区的内容是不可以改变的。
char i[ ] = "test";此时的i是在栈区,内容可以被修改
char * i = "test";此时的i是在文字常量区,内容不可以被修改
内存操作函数
内存操作函数遇到0会\0都不会结束操作,而 str 字符串处理函数遇到\0都会结束
memset
将指定内存区域的前 n
个字节设置为特定的值。memset
并不关心数据是否为字符串,它只是简单地设置指定内存块的值.
char buffer[10];
memset(buffer, 0, sizeof(buffer)); // 将 buffer 的前10个字节设置为0
注意: memset
只是按字节设置,不会在遇到 \0
字符时停止操作,它会一直设置指定的字节数。
memcpy
将源内存区域的数据复制到目标内存区域,通常用于复制非字符串数据。memcpy
也不关心数据是否为字符串,它只是按字节复制数据。
char src[] = "Hello";
char dest[10];
memcpy(dest, src, sizeof(src)); // 将 src 复制到 dest
注意: memcpy
仅复制指定的字节数,不会考虑字符串的终止符 \0
。
内存泄漏
内存申请,不释放,导致程序使用内存空间一直增长
内存污染
向没有申请过的内存空间写入数据。例如:int * p = 100;p并没有指向的地址。