1. 基础知识
.h .hpp 声明文件
.c .cpp 实现文件
#include 后面跟 "" 表示寻找自己写的资源 后面跟<>表示寻找系统的资源
2. 打印:需要占位
printf("i的值:%d\n",i); // int 类型
printf("i的值:%lf\n",d); // double类型
printf("i的值:%f\n",f); // float
printf("i的值:%d\n",l); // long
printf("i的值:%d\n",s); // short
printf("i的值:%c\n",c);// char
printf("i的地址:%p\n",c);// p : 地址占位符`
3. 字符串类型
char * str = "derry";
//打印:
printf("i的值:%s\n",str);// 字符串
4. 常用的api
sizeof() 获取字节数
sizeof(int); // int类型数据所占字节数
sizeof(double); // double类型数据所占字节数
sizeof(char); // char类型数据所占字节数
5.指针(万物皆地址,指针 == 地址)
5.1 地址:
int num = 10000;
printf("number变量的地址:%p\n",&num); // %p 地址占位符 & 取地址符
num指向10000的地址 `
5.2 指针:
& 取地址符; * 取值.
int num_int = 100;
double num_double = 200;
printf("num_int的值:%d\n",num_int);
printf("num_double的值:%lf\n",num_double);
printf("num_int的值:%d\n",*(&num_int));
printf("num_int的值:%lf\n",*(&num_double));
5.3 指针变量: int * | double *
// intP是指针变量,里面存放的是地址:是number_int的地址 * 是取值 取出该地址中的值
int * intP = &number_int;
double * doubleP = &number_double;
printf("num_int的值:%d\n",*intP); // 打印的是number_int的值
printf("num_int的值:%lf\n",*doubleP);
6.函数不能写在main的下面.
如果非要写在main的后面,要在main之前声明一下:
void change(int i);
int main(){
int i = 100;
change(i);
printf("%d,%p\n",i,&i);
return 0;
}
// 要和之前声明的一致
void change(int i){
i = 200;
printf("%d,%p\n",i,&i);
}
打印结果:
200,0032FD8C
100,0032FD98
change函数中的行为参数,和main方法中传递进来的不是同一个参数,调用change方法时,change函数进栈。C/C++编译器会构建一个新的行参,和main函数的i没有半毛钱的关系,内存地址不同,无法修改。
解决: 要改掉main函数的i的值,需要找到main函数中i的地址:
void change(int * i); // 参数是指针类型的变量
int main(){
int i = 100;
change(&i); // 传入的是i的地址
printf("%d\n",i)
return 0;
}
void change(int * i){
*i = 666; // 取出i地址对应的值修改为666
}
输出结果:666
7. 互换数值:
void changeAction(int * a, int * b){
int temp = *a; // 取出a地址对应的值 赋值给temp
*a = *b ; // 取出b地址对应的值,赋值给a地址对应的值
*b = temp; // temp赋值给b地址对应的值
}
int main(){
int a = 100;
int b = 200;
changeAction(&a , &b);
printf("互换后的效果:%d,%d\n",a,b)
}
8.多级指针:
int num = 999;
int * num_p = # // 一级指针 存放num的地址
int ** num_p_p = &num_p; // 二级指针 存放num_p的地址
// 取值: **num_p_p 取出的值是:999
9.数组与指针:
数组的内存地址 = 第一元素的内存地址
int arr[] = {1,2,3,4};
int i = 0;
for(i = 0; i < 4;++i){
printf("%d\n",arr[i]);
}
printf("arr = %p\n",arr);
printf("&arr = %p\n",&arr);
printf("&arr[0] = %p\n",&arr[0]);
// 打印出来 三者相同
// 数组就是一个内存地址
int * arr_p = arr;
printf("%d\n",*arr_p); // 取出内存地址arr_p的值 : 1
arr_p++; // 指针往数组的下一个移位
指针占用的内存大小: 4个字节 (32位系统) ; 4*2 = 8字节 (64位系统)
10.函数指针:万物皆指针,函数也有自己的指针 。
如何定义:
返回值(*名称)(参数类型,参数类型)
即: void(*method)(int,int)
void add(int num1,int num2){
printf("num1 + num2 = %d\n",(num1 + num2));
}
// void(*method)(int,int) 就是 函数指针
void operate(void(*method)(int,int),int num1,int num2)){ method(num1,num2);
}
int main(){
operate(add,1,2); // 调用add函数
return 0;
}
C语言速查文档:文档中 传入 &t &p 可以直接传入NULL尝试
11.C中的boolean类型
非0即true , 0 == false
12.静态开辟
函数进栈时,定义数组或变量,就是属于静态开辟内存,静态开辟的内存空间大小,不能修改。 栈区的最大值是2M,大于2M会栈溢出。
void staticAction(){
int arr[5]; // 开辟 4*5 = 20 字节
for(int i = 0;i<5;++i){
arr[i] = i ;
printf("%d,%p\n",*(arr+i),arr+i);
}
}
// 虽然在死循环中开辟内存,但不会导致栈溢出异常,因为每次调用进栈,每次调用完毕就出栈
// 出栈后会释放所有的栈成员
int main(){
while(9){
sleep(100);
staticAction(); // 在死循环中调用staticAction 每次调用都会进栈,静态开辟内存
}
return 0;
}
13.动态开辟:
使用malloc等开辟堆区的内存,凡是在堆区开辟内存空间,都属于动态开辟。 堆区的最大值没有确定值,但基本上不用担心溢出。
动态开辟的内存,不会被释放
// C开发的过程中,不能出现悬空指针和野指针,把二者置为NULL即可
// int * p = NULL;
void dynamicAction(){
int * p ; // 野指针,没有地址的指针
int * arr = malloc(1 * 1024 * 1024);
printf("dynamicAction函数,arr自己的内存地址:%p,堆区开辟的内存地址:%p\n",&arr,arr);
// 重复释放会导致崩溃,所以先判断一下
if(arr){
free(arr);
// 释放之后,需要把arr指向NULL 否则会出现悬空指针
// 也就是 arr指向一块被释放掉的内存
arr = NULL; // NULL :0x0000
}
}
int main(){
while(9){
sleep(100);
dynamicAction();
}
}
动态开辟的使用场景:
int * arr = malloc(sizeof(int) * 8); // 动态开辟8 * 4 个字节的大小
for(int i = 0;i < 8;i++){
arr[i] = i;
}
动态开辟 realloc 在堆中追加开辟内存
使用方法:
int * arr = (int *)realloc(arr,sizeof(int)*(num + new_num));
realloc(void * 首次开辟的指针,size总大小);
//1.新开辟的内存的指针大部分情况下和首次开辟的指针一样。
//2.传入参数解释:首次开辟的指针、size总大小,传这两个参数的目的为了避免内存空间不够,
//3.当内存不够时,首次开辟指针是为了copy前面的几个数值。
字符串的两种表现方式:
int main(){
// '\0'是给printf的,printf遇到\0才会停止打印 否则会在derry后面输出系统值
char str[] = {'D','e','r','r','y','\0'};
str[2] = 'z';
printf("第一种方式:&s\n",str);
// 运行这段代码时,会造成崩溃,因为str2是一个地址值,
// 指向的是全局区里面的数据,全局区里面的数据拒绝访问
char * str2 = "Derry";
str2[2] = 'z';
printf("第二种方式:%s\n",str);
}
指针挪动获取字符串信息:
int getLen(char * string){
int count = 0;
while(*string){ // *string != '\0' 我就一直循环
string++;// 指针挪动 string 是数组,数组string指向的是数组第一个数值所在的地址
count++;
}
return count;
}
// 这种方式无法获得真正的len
void getLen2(int intarr[]){
int len = sizeof(intarr)/sizeof(int);
printf("getLen的长度是%d\n",len); // 这里打印1
}
void getLen3(int * resultLen,int intarr[]){
// 手动计算长度
int count = 0;
while(*intarr){
intarr++;
count++;
}
*resultLen = count; //找到resultLen地址 , 把count值放入resultLen中
}
int main(){
char string[] = {'A','B','C','D','0','\0'}
int r = getLen(string);
printf("长度是:%d\n",r);// 输出结果是5
int intarr[] = {1,2,3,4,5,6,7};
int len = sizeof(intarr)/sizeof(int);
print("len长度是:%d\n",len); // 打印7
// c/c++ 编译器,数组作为参数传递,会把数组优化成指针(为了高效率)
getLen2(intarr);
int result;
getLen3(&result,intarr); // &result 取出result地址 给函数,函数再给地址赋值
printf("getLen的长度是:%d\n",result);
return 0;
}
CLion快捷键: 两次shift 输入File Encoding 改变Encoding
14.字符串的操作
14.1 字符串的转换
14.1.1 atoi 转换成整型
int main(){
char * num = "1"; // 输出1
num = "12.68xx"; // 输出12
int result = atoi(num);
if(result){ // 非0即true 不是0进入if 0就是转换失败了
printf("恭喜你转换成功:%d\n",result)
}
}
atof 转换成double类型 (使用方法同上)
14.2 字符串的比较
方法 :使用函数strcmp(区分大小写) ,返回0表示相等,返回1不相等 strcmpi(不区分大小写)
14.3 字符串的查找、包含、拼接
方法:使用函数strstr,在串中查找指定字符串的第一次出现的位置,返回指针。是否包含,也使用这个函数。
char * pop = strstr(str1,str2);
if(pop){ // 非NULL 进入 查找到了
printf("查找到了,pop的值是:%s\n",pop);
}else{
printf("没有查找到\n");
}
14.4 字符串的拼接:
方法:使用函数strcat
char destination[25]; // 容器 大小25
char * blank = "--到--" , *CPP = "C++" , *Java = "Java";
strcpy(destination,CPP); // 先copy到数组里面去
strcat(destination,blank);// 然后 再拼接
strcat(destination,Java);// 然后再拼接
printf("拼接后的结果:%s\n",destination);
14.5 大小写转换
void lower(char * dest,char * name){
char * temp;// 避免破坏name指针
while(temp){
*dest = tolower(*name);
temp++;// 挪动指针的位置++
dest++; // 挪动指针的位置 ++ 挪动一个存储一个 挪动一个存储一个
}
*dest = '\0'; // 指针最后一个赋值'\0' 避免printf打印系统值
}
int main(){
char * name = "Derry";
// 先定义结果
char dest[20];
lower(dest,name);
printf("小写转换后的结构是:%s\n",dest);
return 0;
}
14.6 字符串截取
void subStr1(char * result,char * str,int start,int end){
char * temp = str;// 定义临时指针,不破坏str指针
int count = 0; // 记录当前的位置
while(*temp){
if(count > start && count < end){
*result = *temp;
result++; // 接收值也要挪动,挪动指针来接收 temp给我的值
}
temp ++;
count ++;
}
}
// 二级指针,使用的时候传递地址进来 使用&取地址
void subStr2(char ** result,char * str,int start,int end){
char * temp = str; //定义临时指针,不破坏str
// char resultArr[end - start];// 栈内存,在方法结束时,会被释放
// 开辟堆内存
char * resultArr = malloc(end - start);
int count = 0;
for(int i = start;i<end;++i){
resultArr[count] = *(temp+i);
count++;
}
// 取出二级指针中存放的一级指针
*result = resultArr;
printf("%s\n",resultArr);
// 这种方式,方法调用完之后要回收堆空间
}
void subStr3(char * result,char * str,int start,int end){
for(int i = start; i < end ;++i){
*(result +i) = *(str+i);
}
}
void subStr4(char * result,char * str,int start,int end){
// 参数1 : 最终copy到result容器里面
// 参数2: 直接指针挪动到r
// 参数3: 从r开始挪动,挪动多少
strncpy(result,str+start,end-start);
}