1.calloc 和 realloc
1.1calloc 和malloc 都是在堆区分配内存
1.2与malloc不同的是,calloc会将空间初始化为0
1.3calloc(个数,大小)
1.4realloc 重新分配内存
1.4.1如果重新分配的内存比原来大,那么不会初始化新空间为0
1.4.2先看后续空间,如果足够,那么直接扩展
1.4.3如果后续空闲空间不足,那么申请足够大的空间,将原有数据拷贝到新空间下,释放掉原有空间,将新空间的首地址返回
1.4.4如果重新分配的内存比原来小,那么释放后序空间,只有权限操作申请空间
//calloc
void test01()
{
//int * p = malloc(sizeof(int)* 10); // 没有初始化操作,打印出来会是乱码
int * p = calloc(10, sizeof(int)); //calloc 分配在堆区,与malloc不同的是 calloc会初始化数据为0
for (int i = 0; i < 10;i++)
{
printf("%d\n", p[i]);
}
if ( p != NULL)
{
free(p);
p = NULL;
}
}
//realloc 重新分配内存
void test02()
{
int * p = malloc(sizeof(int)* 10); // 先分配一个小内存
for (int i = 0; i < 10;i++)
{
p[i] = i + 100;
}
for (int i = 0; i < 10; i++)
{
printf("%d\n", p[i]);
}
printf("%d\n", p);
//如果重新分配的内存比原来大,那么不会初始化新空间为0
p = realloc(p, sizeof(int)* 20) // P代表首地址,第二个参数代表大小,返回一个新的地址
printf("%d\n", p);
for (int i = 0; i < 20; i++)
{
printf("%d\n", p[i]);
}
//如果重新分配的内存比原来小,那么释放后序空间,只有权限操作申请空间
p = realloc(p, sizeof(int)* 5);
printf("%d\n", p);
printf("%d\n", p[0]);
printf("%d\n", p[5]); // 这里是乱码
}
2.sscanf的使用
符号 | 作用 |
---|---|
%*s或%*d | 跳过数据 |
%[width]s | 读指定宽度的数据 |
%[a-z] | 匹配a到z中任意字符(尽可能多的匹配) |
%[aBc] | 匹配a、B、c中一员,贪婪性 |
%[^a] | 匹配非a的任意字符,贪婪性 |
%[^a-z] | 表示读取除a-z以外的所有字符 |
2.1练习
2.1.1将 ip 分别截取到 num1 到 num4中
2.1.2将 123456#zhangtao@abcde 中截取中间的zhangtao有效数据
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//1、%*s或%*d 跳过数据
void test01()
{
char * str = "12345abcde";
char buf[1024] = { 0 };
sscanf(str, "%*d%s", buf); // 这一步做操作,不输出数据
printf("%s\n", buf); // abcde
}
void test02()
{
char * str = "abcde12345"; //忽略遇到空格或者 \t 代表忽略结束
char buf[1024] = { 0 };
sscanf(str, "%*[a-z]%s", buf); // 把a-z之间的数字忽略掉,然后匹配输出
//sscanf(str, "%*s%s", buf); // 什么都没有
printf("%s\n", buf);
}
//%[width]s 读指定宽度的数据
void test03()
{
char * str = "12345abcde";
char buf[1024] = { 0 };
sscanf(str, "%6s", buf); // 12345a
printf("%s\n", buf);
}
//%[a-z] 匹配a到z中任意字符(尽可能多的匹配)
void test04()
{
char * str = "12345abcdeaaa";
char buf[1024] = { 0 };
sscanf(str,"%*d%[a-c]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf); // abc
}
void test05()
{
char * str = "12345abcdeaaa";
char buf[1024] = { 0 };
sscanf(str, "%[0-9]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf); // 12345
}
//%[aBc] 匹配a、B、c中一员,贪婪性(只匹配这几个,不是这几个都不需要匹配)
void test06()
{
char * str = "abcCdef";
char buf[1024] = { 0 };
sscanf(str, "%[abC]", buf); //只要匹配失败,那么就不继续匹配了
printf("%s\n", buf); // ab
}
//%[^a] 匹配非a的任意字符,贪婪性
void test07()
{
char * str = "abcCdef";
char buf[1024] = { 0 };
sscanf(str, "%[^C]", buf);
printf("%s\n", buf); // abc
}
//%[^a-z] 表示读取除a-z以外的所有字符
void test08()
{
char * str = "abcCdef123456";
char buf[1024] = { 0 };
sscanf(str, "%[^0-9]", buf); // abcCdef
printf("%s\n", buf);
}
//练习1 把ip地址里的数字都单独拿出来
void test09()
{
char * ip = "127.0.0.1";
int num1 = 0;
int num2 = 0;
int num3 = 0;
int num4 = 0;
sscanf(ip, "%d.%d.%d.%d", &num1, &num2, &num3, &num4);
printf("%d\n", num1);
printf("%d\n", num2);
printf("%d\n", num3);
printf("%d\n", num4);
}
//练习2 输出#和@之间的有效字符
void test10()
{
char * str = "abcdef#zhangtao@123456";
char buf[1024] = { 0 };
sscanf(str, "%*[^#]#%[^@]", buf); // * 表示忽略 [^#]忽略#之外的符号(到#时这个忽略停止工作) #:忽略# %[^@]匹配@符号之外的符号(到@时停止工作,后面的也不再匹配)
// %*[^#]%*# 忽略 %[^@]匹配
printf("%s\n", buf); // zhangtao
}
//1. 已给定字符串为: helloworld@itcast.cn,请编码实现helloworld输出和itcast.cn输出。
void test11()
{
char * str = "helloworld@itcast.cn";
char buf1[1024] = { 0 };
char buf2[1024] = { 0 };
sscanf(str, "%[a-z]%*[@]%s", buf1, buf2); // 两个匹配之间夹了一个忽略
printf("%s\n", buf1);
printf("%s\n", buf2);
}
3.查找子串
3.1实现mystrstr 自己查找子串功能
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/*
算法优化
memcmp(str , subStr ,3 ) == 0; //内存的对比机制
*/
int myStrstr(char * str , char * subStr)
{
int num = 0;
while (*str != '\0')
{
if (*str != *subStr)
{
num++;
str++;
continue;
}
//创建两个临时指针 做二次对比
char * tmpStr = str;
char * tmpSubstr = subStr;
while (*tmpSubstr != '\0')
{
if (*tmpStr != *tmpSubstr)
{
//匹配失败
num++;
str++;
break;
}
tmpStr++;
tmpSubstr++;
}
if (*tmpSubstr == '\0')
{
//匹配成功
return num;
}
}
return -1;
}
void test01()
{
char * str = "abdnfcdefgdfasdfaf";
int ret = myStrstr(str, "dnf");
if ( ret != -1)
{
printf("找到了子串,位置为:%d\n", ret);
}
else
{
printf("未找到子串\n");
}
}
int main(){
test01();
system("pause");
return EXIT_SUCCESS;
}
4指针的易错点
4.1越界
4.2指针叠加会不断改变指针指向
4.3返回局部变量地址
4.4同一块内存释放多次(不可以释放野指针)
void test01()
{
char * p = malloc(sizeof(char)* 64);
char * pp = p; //通过创建临时指针操作内存,防止出错
for (int i = 0; i < 10;i++)
{
*pp = i + 97; // 原本是*p = i+97
printf("%c\n", *pp);
pp++; //更改指针位置,释放出错
}
if (p!= NULL)
{
free(p);
}
}
5const使用场景
5.1const使用 修饰形参 防止误操作
struct Person
{
char name[64]; // 0 ~ 63
int age; // 64 ~ 67
int Id; // 68 ~ 71
double score; // 72 ~79
};
//将struct Person p 改为 struct Person * p 节省资源,但是指针容易误操作
//const使用 修饰形参 防止误操作
void showPerson(const struct Person *p) // 这个函数目的只是为了打印,不能有数字的更改
{
// 如果使用 struct Person p 值传递,不会更改本体,但是每次都要占用很大的内存
//p.age = 100;
//p->age = 100; // 指针访问指针用->
//printf("姓名: %s 年龄: %d 学号 %d 分数 %f\n", p.name, p.age, p.Id, p.score);
printf("姓名: %s 年龄: %d 学号 %d 分数 %f\n", p->name, p->age, p->Id, p->score);
}
6二级指针做函数参数的输入输出特性
6.1二级指针做函数参数的输入特性
6.1.1创建在堆区
6.1.2创建在栈区
6.2二级指针做函数参数的输出特性
6.2.1被调函数分配内存,主调函数使用
void printArray(int ** pArray , int len)
{
for (int i = 0; i < len;i++)
{
printf("%d\n", *pArray[i]);
}
}
void test01()
{
//创建在堆区
int ** pArray = malloc(sizeof(int *)* 5);
//在栈上创建5个数据
int a1 = 10;
int a2 = 20;
int a3 = 30;
int a4 = 40;
int a5 = 50;
pArray[0] = &a1;
pArray[1] = &a2;
pArray[2] = &a3;
pArray[3] = &a4;
pArray[4] = &a5;
//打印数组
printArray(pArray, 5);
//释放堆区数据
if (pArray != NULL)
{
free(pArray);
pArray = NULL;
}
}
void freeSpace(int **pArray , int len)
{
for (int i = 0; i < len;i++)
{
free(pArray[i]);
pArray[i] = NULL;
}
}
void test02()
{
//创建在栈区
int * pArray[5];
for (int i = 0; i < 5;i++)
{
pArray[i] = malloc(4);
*(pArray[i]) = 10 + i;
}
printArray(pArray, 5);
//释放堆区
freeSpace(pArray,5);
}
```c
void allocateSpace(int ** p)
{
int * temp = malloc(sizeof(int)* 10);
for (int i = 0; i < 10;i++)
{
temp[i] = 100 + i;
}
*p = temp;
}
void printArray(int ** pArray, int len)
{
for (int i = 0; i < len;i++)
{
printf("%d\n", (*pArray)[i]);
}
}
void freeSpace(int ** pArray)
{
if (*pArray != NULL)
{
free(*pArray);
*pArray = NULL;
}
}
void test01()
{
int * p = NULL;
allocateSpace(&p);
printArray(&p, 5);
freeSpace(&p, 5);
if (p == NULL)
{
printf("空指针\n");
}
else
{
printf("野指针\n");
}
}
7二级指针练习-文件操作
7.1读取配置文件信息,并且将信息存放到 数组中
7.2注意: 释放堆区,关闭文件
//获取有效行数
int getFileLines(FILE * pFile)
{
if (pFile == NULL)
{
return -1;
}
char buf[1024] = { 0 };
int lines = 0;
while (fgets(buf,1024,pFile) != NULL)
{
//printf("%s", buf);
lines++;
}
//将文件光标置首
fseek(pFile, 0, SEEK_SET);
return lines;
}
//读取数据放入到pArray中
void readFileData(FILE * pFile, int len, char ** pArray)
{
if (pFile == NULL)
{
return;
}
if (len <= 0 )
{
return;
}
if ( pArray ==NULL)
{
return;
}
char buf[1024] = { 0 };
int index = 0;
while (fgets(buf, 1024, pFile) != NULL)
{
/*
aaaaaaaaaa
bbbb
cccccc
*/
int currentLen = strlen(buf) +1 ;
char * currentStrP = malloc(sizeof(char)* currentLen);
strcpy(currentStrP, buf);
pArray[index++] = currentStrP;
memset(buf, 0, 1024);
}
}
void showFileData(char ** pArray, int len)
{
for (int i = 0; i < len;i++)
{
printf("%d行的数据为 %s", i + 1, pArray[i]);
}
}
void test01()
{
//打开文件
FILE * pFile = fopen("./test.txt", "r");
if (pFile == NULL)
{
printf("文件打开失败\n");
return;
}
//统计有效行数
int len = getFileLines(pFile);
//printf("文件的有效行数为:%d\n", len);
char ** pArray = malloc(sizeof(char *)* len);
//读取文件中的数据并且放入到 pArray中
readFileData(pFile, len, pArray);
//读取数据
showFileData(pArray , len);
//释放堆区内容
for (int i = 0; i < len; i++)
{
if (pArray[i] != NULL)
{
free(pArray[i]);
pArray[i] = NULL;
}
}
free(pArray);
pArray = NULL;
//关闭文件
fclose(pFile);
}
8位运算
8.1按位取反 ~ 0变1 1 变0
8.2按位与 & 全1为1 一0为0
8.3按位或 | 全0为0 一1为1
8.4按位异或 ^ 相同为0不同为1
/按位取反 ~
void test01()
{
int num = 2;
// 010 取反
// 101 源码
//计算机用补码方式存数据 110 + 1 = 111
printf("~num = %d\n", ~num); // -3
}
//按位与 &
void test02()
{
int num = 123;
if ( (num & 1 ) == 1 )
{
printf("num为奇数\n");
}
else
{
printf("num为偶数\n");
}
}
//按位或 |
void test03()
{
int num1 = 5;
int num2 = 3;
printf("num1 | num2 = %d\n", num1 | num2); // 7
}
//按位异或 ^
void test04()
{
int num1 = 5;
int num2 = 9;
/*int tmp = num1;
num1 = num2;
num2 = tmp;*/
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
//num1 = num1 + num2;
//num2 = num1 - num2;
//num1 = num1 - num2;
printf("num1 = %d\n", num1);
printf("num2 = %d\n", num2);
}
//左移运算 <<
void test05()
{
int num = 20; // 20 * 2 ^ 2;
printf("%d\n", num <<= 2);
}
//右移运算 >>
void test06()
{
int num = 20; // 20 / 2
printf("%d\n", num >>= 1);
}
9位移运算
9.1左移运算 << X 乘以2 ^ X
9.2右移运算 >> X 除以 2 ^X
9.2.1有些机器用0填充高位
9.2.2有些机器用1填充高位
9.2.3如果是无符号,都是用0填充