calloc
void *calloc(size_t namber,size_t size);
功能:在内存动态存储区中分配namber块长度为size字节的连续区域。calloc自动将分配的内存置0
namber:所需内存单元数量
size:每个内存单元的大小(单位:字节)
返回值:
成功:分配空间的起始地址
失败:NULL
/** calloc **/
void test()
{
int *p = calloc(10,sizeof(int));
for(int i = 0;i < 10;++i)
{
p[i] = i+1;
}
//释放内存
if(p != NULL)
{
free(p);
p = NULL;
}
}
realloc
void *realloc(void *ptr, size_t size);
功能:重新分配用malloc或者calloc函数在堆中分配的内存空间大小。
realloc不会自动清理增加 的内存,需要手动清理,如果指定的地址后面又连续的空间,那么就会在已有地址基础上增加内存,如果指定的地址后面没有空间,那么realloc会重新分配新的连续内存,把旧内存的值拷贝到新内存,同时释放旧内存
ptr:为之前用malloc或者calloc分配的内存地址,如果此参数等于NULL,那么和realloc与calloc功能一致
size:为重新分配内存的大小,单位:字节
返回值:
成功:新分配的堆内存地址
失败:NULL
/**realloc**/
void test()
{
int *p = malloc(sizeof(int)*10);
for(int i = 0;i < 10;++i)
{
p[i] = i + 1;
}
p = realloc(p,sizeof(int)*20);//扩展20个int类型大小的内存空间
}
sscanf
int sscanf(const char *str,const char *format,...);
功能:从str指定的字符串读取数据,并根据参数format字符串类转换并格式化数据。
str:指定的字符串首地址
format:字符串格式,用法和scanf()一样
返回值:
成功:实际读取的字符个数
失败:-1
格式 | 作用 |
---|---|
%*s或%*d | 跳过数据(%*s跳过字符串、%*d跳过数字) |
%[width]s | 读指定宽度的数据 |
%[a-z] | 匹配a到z中任意字符(尽可能多的匹配) |
%[aBc] | 匹配a、B、c中一员,贪婪性 |
%[^a] | 匹配非a的任意字符,贪婪性 |
%[^a-z] | 表示读取除a-z以外的所有字符 |
//%*s或%*d 跳过数据
void test()
{
#if 0
char *str = "12345abcde";
char buf[1024] = { 0 };
sscanf(str,"%*d%s",buf);//忽略数字并且匹配字符串
printf("buf:%s\n",buf);//buf:abcd
#end if
char *str = "abcde 12345";//字符串和数字之间需要加空格
char buf[1024] = { 0 };
sscanf(str,"%*s%d",buf);//忽略字符串匹配数字
printf("buf:%s\n",buf);//buf:12345
}
//%[width]s 读取指定宽度的数据
void test01()
{
char *str = "12345abcde";
char buf[1024] = { 0 };
sscanf(str,"%6s",buf);//读取6个数据
printf("buf:%s\n",buf);//buf:12345a
}
//%[a-z]匹配a到z中任意字符(尽管可能多的匹配)
void test02()
{
//char *str = "12345abcde";
char *str = "abcde12345";
/**
在使用匹配的时候如果发现第一个数据不匹配那么就会自动退出,
因此需要将字母放在前面才能匹配到想要的结果
**/
char buf[1024] = { 0 };
sscanf(str,"%[a-z]",buf);//匹配a到z中任意字符(尽管可能多的匹配)
printf("buf:%s\n",buf);//buf:abcde
}
// %[aBc] 匹配a、B、c中一员,贪婪性
void test03()
{
char *str = "aABbcde";
char buf[1024] = { 0 };
sscanf(str,"%[aAb]",buf);//匹配a、B、c中一员,贪婪性
printf("buf:%s\n",buf);//buf:aA
}
//%[^a] 匹配非a的任意字符,贪婪性
void test04()
{
char *str = "aABbcde";
char buf[1024] = { 0 };
sscanf(str,"%[^c]",buf);//匹配非c的字符
printf("buf:%s\n",buf);//buf:aABb
}
//%[^a-z] 表示读取除a-z以外的所有字符
void test05()
{
char *str = "aABbcde12345";
char buf[1024] = { 0 };
sscanf(str,"%[^0-9]",buf);//匹配非0-9的字符
printf("buf:%s\n",buf);//buf:aABbcde
}
void test06()
{
char *str = "abcde#12uip@0plju";
char buf[1024] = {0};
sscanf(str,%*[^#]#%[^@],buf);
printf("buf:%s\n",buf);//buf:12uip
}
//查找子串第一次出现的位置
void myStrStr( const char *str,const char *substr)
{
const char *mystr = str;
const char *mysub = substr;
while(*mystr != '\0')
{
if(*mystr != *mysub)
{
++mystr;
continue;
}
//临时指针变量
char *temp_mystr = mystr;
char *temp_mysub = mysub;
while(*temp_mystr != '\0')
{
if(*temp_mystr != *temp_mysub)
{
++mystr;
break;
}
++temp_mysub;
++temp_mystr;
}
if(*temp_sub == '\0')
{
return mystr;
}
++mystr;
}
return NULL;
}
const的使用
const变量放在常量区
/**
一般函数传递参数的时候都不进行值传递
大部分使用地址传递
并且在哪函数中如果传入参数不涉及值修改
可以在其参数之前加上const关键字
以防参数值被修改
**/
struct Person
{
char name[64];
int age;
int ID;
double source;
};
#if 0
void PrintPerson(const struct Person person)
{
printf("Name:%s Age:%d ID:%d source:%d\n",person.name,person.age,person.ID,person.source);
}
#else if
void PrintPerson(const struct Person *person)
{
printf("Name:%s Age:%d ID:%d source:%d\n",person->name,person->age,person->ID,person->source);
}
#end if
void test()
{
struct Person person = {"trump",30,250,59.9};
#if 0
PrintPerson(person);
#else if
PrintPerson(&person);
#end if
}
指针的指针(二级指针)
指针的指针:存储的值是另一个指针的地址
/**
二级指针做函数参数的输出特性
**/
void allocateSpace(int **temp)
{
int *arr = malloc(sizeof(int)*10);
for(int i = 0;i < 10;++i)
{
arr[i] = i+1;
}
//指针间接赋值
*temp = arr;
}
void printArray(int *array,int len)
{
for(int i = 0;i < len;++i)
{
printf("%d ",array[i]);
}
}
void test()
{
int *pArray = NULL;
allocateSpace(pArray);
printArray(&pArray,10);//1 2 3 4 5 6 7 8 9 10
}
/**
二级指针做形参输入特性:由主调函数分配内存
**/
void printArray(int **arr,int len)
{
for(int i = 0;i < len;++i)
{
printf("%d ",*arr[i]);
}
}
/**
在堆上开辟指针数组
在栈上开辟指针内存
**/
void test()
{
//堆上分配指针数组
int **pArraay = malloc(sizeof(int *)*6);
//栈上分配数据空间
int a1 = 100;
int a2 = 200;
int a3 = 300;
int a4 = 400;
int a5 = 500;
int a6 = 600;
#if 0
pArray[0] = &a1;
pArray[1] = &a2;
pArray[2] = &a3;
pArray[3] = &a4;
pArray[4] = &a5;
pArray[5] = &a6;
#end if
*(pArray + 0) = &a1;
*(pArray + 1) = &a2;
*(pArray + 2) = &a3;
*(pArray + 3) = &a4;
*(pArray + 4) = &a5;
*(pArray + 5) = &a6;
printArray(pArray,6);//100 200 300 400 500 600
}
/**
在栈上开辟指针数组
在堆上开辟指针内存
**/
void test01()
{
int *pArray[5];
for(int i = 0;i < 5;++i)
{
pArray[i] = maloc(4);
*(pArray[i]) = 100+i;
}
printfArray(pArray,5);//100 101 102 103 104
//释放堆内存
for(int i = 0;i < 5;++i)
{
if(pArray[i] != NULL)
{
free(pArray[i]);
pArray[i] = NULL;
}
}
}
二级指针文件读写
读取文件数据,将文件内数据每一行读取出来,然后在按照行为单位存储文件数据
//获得文件行数
void getFileLines(FILE *file)
{
if(NULL == file) return -1;
char buf[1024] = {0};
int lines = 0;//文件行数
while(fgets(file, 1024,file) !=NULL)
{
++lines;
}
//恢复文件指针,使文件指针从文件末尾指到文件起始位置
fseek(file,0,SEEK_SET);
return lines;
}
//读取文件数据
void readFileData(FILE *file,int lines,char **contents)
{
if(NULL == file)
{
return ;
}
if(NULL == contents) return ;
if(lines <= 0) return ;
//创建缓冲区
char buf[1024] = {0};
int index = 0;
while(fgets(buf,1024,file) != NULL)
{
//计算当前行字符串长度
int curLineLen = strlen(buf) + 1;
//给当前行分配内存
char *lineContent = malloc(sizeof(char)* curLineLen);
//将行数据拷贝到空间中
strcpy(lineContent, buf);
contents[index++] = lineContent;
memset(buf,0,1024);
}
}
void showFileContents(char **contents,int lines)
{
for(int i = 0;i < lines;++i)
{
printf("%d行:%s",i+1,contents[i]);
}
}
//释放文件数据内存
void freeFileSpace(char **contents,int lines)
{
for(int i = 0;i < lines;++i)
{
if(contents[i] != NULL)
{
free(contents[i]);
contents[i] = NULL;
}
}
free(contents);
contents = NULL;
}
void test()
{
//打开文件
FILE *file = fopen("./text.txt","r");
if(NULL == file)
{
printf("文件打开失败\n");
return ;
}
//统计文件行数
int lines = getFileLines(file);
//堆上开辟空间存储每行数据
char **pContents = malloc(sizeof(char *)*lines);
//读取文件内容
readFileDate(file,line,pContents);
//关闭文件
fclose(file);
file = NULL;
//打印文件内容
showFileContents(pContents, lines)
//释放文件数据内存
freeFileSpace(pContents,lines)
}
位运算
位运算一般不能对浮点数进行计算,一般对char、short、int、long类型进行使用。
/**
按位取反 ~
**/
void test()
{
int number = 2;//010
printf("~number : %d\n",~number);// -3
/**
~010 -> 101
负数使用补码存储,101是负数因此要将其转换成补码,得保证符号位不变,其余位置取反,末尾加一
最终结果111,第一位1是负号,因此结果是-3
**/
}
/**
按位与 & 将指定位置置成0
10011&00010 -> 00010
**/
void test()
{
int number = 335;
if(number & 1 == 0) printf("%d是偶数",number);
else printf("%d是奇数",number);
//number清零操作
number = number&0;
}
/**
按位或 | 可以将指定位置置成1
1101 | 0011 -> 1111
**/
void test()
{
int number1 = 5,number2 = 3;
printf("number1 | number2 = %d\n",number1 | number2);//7
}
/**
位异或 ^
A:10101110 ^ B:11011100 --> R:01110010
A ^ B = R;
R ^ B = A;
R ^ A = B;
A ^ B ^ B = A;
**/
void test()
{
int num1 = 5,num2 = 9;
#if 0
int temp = num1;
num1 = num2;
num2 = temp;
#endif
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
printf("num1:%d num2:%d\n",num1,num2);//num1:9 num2:5
}
移位运算符
001 << 1 = 2
010 << 1 = 4
100 << 1 = 8
/**
左移运算符 <<
左移几位相当于乘以2的几次方
将其左侧操作数的值的每一位向左移动,移动的位数由其右侧操作数指定,
空出来的位用0填充,斌且丢弃一处左侧操作数末端的数
**/
void test()
{
int number = 20;
printf("number = %d\n",number << 2);//number = 80
}
/**
右移几位相当于除以2的几次方
右移运算符 >> 将其左侧的操作数的值的每位向右移动
移动的位数由其右侧的操作数指定
丢弃移除左侧操作数右端的位
对于unsigned类型,使用0填充左端空出的位
对于有符号类型,结果依赖于机器
空出的位可能用0填充,或者使用符号(最左端)位的副本填充
**/
//有符号值
(10001010) >> 2
(00100010) //在某些系统上的结果值
(10001010) >> 2
(11100010) //在另一些系统上的结果
//无符号值
(10001010) >> 2
(00100010) //所有系统上的结果
一维数组
/**
数组的两种情况下不是指向数组首元素地址的指针, 数组名是数组类型
1.sizeof
2.对数组名取地址&arr
除了以上两点之外,数组名在其他任何情况下都是指向首元素的指针
数组名是一个常量指针
**/
void test()
{
int arr[] = {1,2,3,4};
printf("sizeof:%d\n",sizeof arr);//sizeof:16
printf("&arr addr:%d\n",&arr+1);//&arr addr:7208104
printf("&arr + 1 addr:%d\n",&arr+1);//&arr + 1 addr:7208120
}
/**
定义一个指向数组的指针
**/
void test()
{
//1.先定义数组类型,再定义数组指针类型
int arr[5] = {1,2,3,4,5};
typedef int (ARRAY_TYPE)[5];
ARRAY_TYPE myarray;//int myarray[5];
memset(myaray,5,10);
//对数组名取地址代表指向整个数组的指针
ARRAY_TYPE *pArray = &myarray;
pArray = &arr;
/**
1. *pArray 表示拿到pArray指针指向的整个数组
2. *pArray类型就是数组名,指向首元素类型的指针
**/
//2.直接定义数组指针类型
typedef int(*ARRAY_POINT)[5];
ARRAY_POINT pArr = &arr;
//3.直接定义数组指针变量
int(*pArrParam)[5] = &arr;
}
多维数组
一维数组名是一个指针常量,它的类型是“指向元素类型的指针”,他指向数组的第一个元素,多维数组也是同理,多维数组的数组名也是指向第一个元素,只不过第一个元素是一个数组
/**
**/
void test()
{
int arr[3][3] =
{
{1,2,3},
{4,5,6},
{7,8,9}
}
#if 0
int arr[3][3] = {1,2,3,4,5,6,7,8,9};
int arr[][3] = {1,2,3,4,5,6,7,8,9};
#endif
/**
对于二维数组同一维数组一样,除了sizeof对数组名求地址之外,数组名就是指向数组首元素的指针
**/
printf("%s\n",typeid(arr).name());//int [3][3]
}
指针数组排序—选择排序
使用双重for循环,内循环找到每次最小的值,找到之后和第一位相交换
void selectSort(int *arr,int len){
for(int i = 0;i < len;++i){
int min = i;
for(int j = i;j < len;++j){
if(arr[min] > arr[j]) min = j;
}
if(min != i)
{
int temp = arr[min];
arr[min] = arr[i];
arr[i] = temp;
}
}
}
结构体赋值
//栈内存
struct Person
{
char name[64];
int age;
};
void test()
{
/**
等号赋值拷贝是数据复制拷贝
就是将person1的name、age数据拷贝到person2的name、age
**/
struct Person person1 = {"aaa",12};
struct Person person2 = {"bbb",30};
person2 = person1;
}
//堆内存
struct Teacher
{
char name[64];
int age;
};
void test()
{
struct Teacher teacher1;
teacher1.name = malloc(sizeof(char)*64);
memset(teacher1.name,0,64);
strcpy(teacher1.name,"aaa");
teacher1.age = 20;
struct Teacher teacher2;
teacher2.name = malloc(sizeof(char)*128);
memset(teacher2.name,0,128);
strcpy(teacher2.name,"bbbbbbb");
teacher2.age = 30;
//teacher1和teacher2大小是一样的
#if 0
teacher1 = teacher2;//报错
/**
如果再结构体内部右指针,并且指针指向堆空间
那么如果发生赋值行为,就会产生两个问题:
1.同一块空间被释放两次
2.内存泄漏
因为再进行复制的时候被赋值的结构体原本指向自己堆内存空间的指针
就会被覆盖指向赋值结构体的堆内存空间
此时就会造成没有指针指向原本被赋值结构体的堆内存空间
**/
#endif
//如果结构体内部有指针指向堆内存
//那么就不能使用编译器默认的赋值行为
//应该手动控制赋值过程
if(teacher1.name != NULL)
{
free(teacher1.name);
teacher1.name = NULL;
}
teacher1.name = malloc(strlen(teacher2.name) + 1);
strcpy(teacher1.name,teacher2.name);
teacher1.age = teacher2.age;
}