求非空字符串元素个数:
“ni chou sha chou ni za di”
示例
int no_spa_str(char* str)
{
int count = 0;
while (*str)
{
if (*str != ' ')
{
count++;
}
str++;
}
return count;
}
int main(void)
{
char str[] = "ni chou sha chou ni za di";
int ret = no_spa_str(str);
printf("ret = %d\n", ret);
system("pause");
return EXIT_SUCCESS;
}
字符串逆置: str_inverse
hello -- olleh
void str_inserse(char *str)
{
char *start = str; // 记录首元素地址
char *end = str + strlen(str) - 1; // 记录最后一个元素地址。
while (start < end) // 首元素地址是否 < 最后一个元素地址
{
char tmp = *start; // 三杯水 char 元素交换
*start = *end;
*end = tmp;
start++; // 首元素对应指针后移
end--; // 尾元素对应指针前移
}
}
示例
void arr_inverse (int *arr,int n) //整型数组逆序
{
int i = 0;
int j = n - 1;
int temp = 0;
while (i < j)
{
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
void str_inverse(char* str) //字符串逆序
{
char* start = str; //记录首元素地址;
char* end = str + strlen(str) - 1;//记录最后一个元素地址;
while (start < end)
{
char temp = *start;
*start = *end;
*end = temp;
start++; //首元素对应指针后移;
end--; //尾元素对应指针前移;
}
}
int main(void)
{
int arr[] = { 23,25,45,89,11 };
int n = sizeof(arr)/sizeof(arr[0]);
char str[] = "ni chou sha";
arr_inverse(arr, n); //整型数组逆序;
str_inverse(str); //字符串逆置;
for (size_t i = 0; i < n; i++) //打印逆序后整型数组;
{
printf("%d ", arr[i]);
}
printf("\n");
printf("%s\n", str); //打印逆序后字符串;
system("pause");
return EXIT_SUCCESS;
}
判断字符串是回文:
int str_abcbb(char *str)
{
char *start = str; // 记录首元素地址
char *end = str + strlen(str) - 1; // 记录最后一个元素地址。
while (start < end) // 首元素地址是否 < 最后一个元素地址
{
if (*start != *end) // 判断字符是否一致。
{
return 0; // 0 表示非 回文
}
start++;
end--;
}
return 1; // 1 表示 回文
}
示例
int str_abcba(char* str)
{
char* start = str;
char* end = str + strlen(str) - 1;
while (start < end)
{
if (*start == *end)
{
start++;
end--;
}
else
{
return 0;
}
}
return 1;
}
int main(void)
{
char str[] = "hellolleh";
int ret = str_abcba(str);
if (ret == 1)
{
printf("是回文\n");
}else
{
printf("不是回文\n");
}
system("pause");
return EXIT_SUCCESS;
}
#字符串处理函数: #include <string.h>
字符串拷贝:
strcpy:
将 src 的内容,拷贝给 dest。 返回 dest。 保证dest空间足够大。【不安全】
char *strcpy(char *dest, const char *src);
函数调用结束 返回值和 dest参数结果一致。
strncpy:
将 src 的内容,拷贝给 dest。只拷贝 n 个字节。 通常 n 与dest对应的空间一致。
默认 不添加 ‘\0’
char *strncpy(char *dest, const char *src, size_t n);
特性: n > src: 只拷贝 src 的大小
n < src: 只拷贝 n 字节大小。 不添加 ‘\0’
示例
int main(void)
{
char src [] = "hello world";
char dest[100] = {0};
char *p =strcpy(dest, src);
printf("dest = %s\n", dest);
printf("p=%s\n", p);
char dest1[100] = { 0 };
char* p1 = strncpy(dest1, src, 9);
printf("dest = %s\n", dest1);
printf("p=%s\n", p1);
system("pause");
return EXIT_SUCCESS;
}
字符串拼接:
strcat:
将 src 的内容,拼接到 dest 后。 返回拼接后的字符串。 保证 dest 空间足够大。
char *strcat(char *dest, const char *src);
strncat:
将 src 的前 n 个字符,拼接到 dest 后。 形成一个新的字符串。保证 dest 空间足够大。
char *strncat(char *dest, const char *src, size_t n);
函数调用结束 返回值和 dest 参数结果一致。
示例
int main(void)
{
char src[] = "hello world";
char dest[] = "we are saying ";
char* p = strcat(dest, src);
printf("dest = %s\n", dest);
printf("p=%s\n", p);
char dest1[] = "we are saying ";
char* p1 = strncat(dest1, src, 5);
printf("dest = %s\n", dest1);
printf("p=%s\n", p1);
system("pause");
return EXIT_SUCCESS;
}
字符串比较: 不能使用 > < >= <= == !=
strcmp:
比较s1和s2两个字符串,如果相等 返回0.如果不相等,进一步表 s1 和 s2 对应位 ASCII码值。
s1 > s2 返回1
s1 < s2 返回-1
int strcmp(const char *s1, const char *s2);
strncmp:
int strncmp(const char *s1, const char *s2, size_t n);
比较s1和s2两个字符串的前n个字符,
如果相等 返回0。如果不相等,进一步表 s1 和 s2 对应位 ASCII码值。(不比字符串ASCII码的和)
s1 > s2 返回1
s1 < s2 返回-1
示例
int main(void) //字符串比较
{
char *str1 = "helloworld";
char *str2 = "hello";
printf("ret = %d\n", strcmp(str1, str2));
printf("ret2 = %d\n", strncmp(str1, str2,5));
system("pause");
return EXIT_SUCCESS;
}
字符串格式化输入、输出:
sprintf(): s -- string
int sprintf(char *str, const char *format, ...);
对应printf,将原来写到屏幕的“格式化字符串”,写到 参数1 str中。
printf("%d+%d=%d\n", 10, 24, 10+24);
---》
char str[100];
sprintf(str, "%d+%d=%d\n", 10, 24, 10+24); 格式串写入str数组中。
sscanf():
int sscanf(const char *str, const char *format, ...);
对应scanf, 将原来从屏幕获取的“格式化字符串”, 从 参数1 str中 获取。
scanf("%d+%d=%d", &a, &b, &c);
---》
char str[]= "10+24=45";
sscanf(str, "%d+%d=%d", &a, &b, &c);
a --> 10, b --> 24, c --> 45
示例
int main(void)
{
char buf[100] = { 0 };
sprintf(buf, "%d%c%d=%d\n", 10, '+', 34, 10 + 34);
printf(buf);
int a, b, c;
// scanf("%d+%d=%d", &a, &b, &c);
char str[] = "13+25=58";
sscanf(str,"%d+%d=%d", &a, &b, &c);
printf("a=%d\n", a);
printf("b=%d\n", b);
printf("c=%d\n", c);
system("pause");
return EXIT_SUCCESS;
}
字符串查找字符、子串:
strchr():
在字符串str中 找一个字符出现的位置。 返回字符在字符串中的地址。
char *strchr(const char *s, int c);
printf("%s\n" strchr("hehehahahoho", 'a')); --> "ahahoho"
strrchr():
自右向左,在字符串str中 找一个字符出现的位置。 返回字符在字符串中的地址。
char *strrchr(const char *s, int c);
printf("%s\n" strrchr("hehehahahoho", 'a')); --> "ahoho"
strstr():
在字符串str中,找子串substr第一次出现的位置。返回地址。
char *strstr(const char *str, const char *substr);
在字符串中找子串的位置。
printf("%s\n" strrchr("hehehahahoho", "ho")); --> "hoho"
printf("%s\n" strrchr("hehehahahoho", "xixi")); --> NULL
字符串分割:
scanf("%s", str);
scanf("%[^\n]", str);
实际工作中基本上字符串分割用的更多的是正则表达式;
strtok():
按照既定的分割符,来拆分字符串。“www.baidu.com” --> "www\0baidu.com"
char *strtok(char *str, const char *delim);
参1: 待拆分字符串
参2: 分割符组成的“分割串”
返回:字符串拆分后的首地址。 “拆分”:将分割字符用 '\0'替换。
特性:
1)strtok拆分字符串是直接在 原串 上操作,所以要求参1必须,可读可写(char *str = "www.baidu.com" 不行!!!)
2)第一次拆分,参1 传待拆分的原串。 第1+ 次拆分时,参1传 NULL.
示例
int main(void)
{
char str[] = "www.itcast.cn.com.net";//www itcast cn
char* p = strtok(str, "."); //第一次拆分,参1 传待拆分的字符串
while (p != NULL)
{
p = strtok(NULL, "."); //第1+次拆分时,参1传NULL,会自动链接到拆分后串的地址
printf("%s\n", p);
}
system("pause");
return EXIT_SUCCESS;
}
练习: 拆分 ".itcast.cntest"
char str[] = "www.itcast.cn$This is a strtok$test";
char *p = strtok(str, "$ .");
while (p != NULL)
{
p = strtok(NULL, " .$");
printf("p = %s\n", p);
}
字符串转换其他数据类型:atoi/atof/atol:
使用这类函数进行转换,要求,原串必须是可转换的字符串。
错误使用:"abc123" --> 0; "12abc345" ---> 12; "123xyz" -->123
atoi:字符串 转 整数。
int atoi(const char *nptr);
atof:字符串 转 浮点数
atol:字符串 转 长整数
示例
int main(void)
{
char str1[] = "10";
int num1 = atoi(str1);
printf("num1 = %d\n", num1);
char str2[] = "0.321";
double num2 = atof(str2);
printf("num2 = %f\n", num2);
char str3[] = "1010L";
int num3 = atol(str3);
printf("num3 = %ld\n", num3);
system("pause");
return EXIT_SUCCESS;
}
局部变量:
概念:定义在函数内部的变量。
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。
全局变量:
概念:定义在函数外部的变量。
作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。
static全局变量:
定义语法: 在全局变量定义之前添加 static 关键字。 static int a = 10;
作用域:被限制在本文件内部,不允许通过声明导出到其他文件。
static局部变量:
定义语法: 在局部变量定义之前添加 static 关键字。
特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。
示例
void test(void)
{
static int b = 1; //相当于b是定义在全局位置,只定义一次;但是作用域只是在函数内;
printf("b = %d\n", b++); //局部静态变量可以被操作;b = 1,..10等,可以作为计数器;
}
int main(void)
{
for (size_t i = 0; i < 10; i++)
{
test();
}
system("pause");
return EXIT_SUCCESS;
}
全局函数: 函数
定义语法: 函数原型 + 函数体
static函数:
定义语法:static + 函数原型 + 函数体
static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。
生命周期:
局部变量:从变量定义开始,函数调用完成。 --- 函数内部。
全局变量:程序启动开始,程序终止结束。 --- 程序执行期间。
static局部变量:程序启动开始,程序终止结束。 --- 程序执行期间。
static全局变量:程序启动开始,程序终止结束。 --- 程序执行期间。
全局函数:程序启动开始,程序终止结束。 --- 程序执行期间。
static函数:程序启动开始,程序终止结束。 --- 程序执行期间。
当全局变量和局部变量重名的时候,采用就近原则
内存4区模型:
代码段:.text段。 程序源代码(二进制形式)。
数据段:只读数据段 .rodata段。初始化数据段 .data段。 未初始化数据段 .bss 段。
stack:栈。 在其之上开辟 栈帧。 windows 1M --- 10M Linux: 8M --- 16M
heap:堆。 给用户自定义数据提供空间。 约 1.3G+
开辟释放 heap 空间:
void *malloc(size_t size); 申请 size 大小的空间
返回实际申请到的内存空间首地址。 【我们通常拿来当数组用】
void free(void *ptr); 释放申请的空间
参数: malloc返回的地址值。
示例
int main(void)
{
int *p=(int *)malloc(sizeof(int) * 10);//申请malloc空间
//char *str = (char *)malloc(sizeof(char)*10);
if (p == NULL) //如果申请失败提示
{
printf("malloc error\n");
return -1;
}
int * tmp = p;//记录malloc返回的地址值,用于free操作;
//写数据到malloc空间;
for (size_t i = 0; i < 10; i++)
{
p[i] = i + 10;
}
//
for (size_t i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//释放申请的内存
free(tmp);
//如果有malloc之后的地址p一定会变化如有p++操作
//提前使用临时变量tmp保存一份用于释放;
tmp =NULL;//通常将free后的地址设为NULL;
system("pause");
return EXIT_SUCCESS;
}
使用 heap 空间:
空间时连续。 当成数组使用。
free后的空间,不会立即失效。 通常将free后的 地址置为NULL。
free 地址必须 是 malloc申请地址。否则出错。
如果malloc之后的地址一定会变化,那么使用临时变量tmp 保存。
二级指针对应的 heap空间:
申请外层指针:
char **p = (char **)malloc(sizeof(char *) * 5);
申请内层指针:
for(i = 0; i < 5; i++)
{
p[i] = (char *)malloc(sizeof(char) *10);
}
使用: 不能修改 p 的值。
for(i = 0; i < 5; i++)
{
strcpy(p[i], "helloheap");
}
释放内层:
for(i = 0; i < 5; i++)
{
free(p[i]);
}
释放外层:
free(p);
示例
int main(void)
{
//申请空间;先申请外层
int** p = malloc(sizeof(int*) * 3);// int **p ==> int *p[3];
//申请内层空间;
for (size_t i = 0; i < 3; i++)
{
p[i] = malloc(sizeof(int) * 5);
}
//使用空间-写;
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 5; j++)
{
p[i][j] = i + j;
}
}
//使用空间-读;
for (size_t i = 0; i < 3; i++)
{
for (size_t j = 0; j < 5; j++)
{
printf("%d ", *(*(p + i) + j));//p[i][j] ==*(p*i)[j] ==*(*(p+i)+j);
}
}
//释放空间;先释放内层空间
for (size_t i = 0; i < 3; i++)
{
free(p[i]);
p[i] = NULL;//不做也无所谓;
}
//释放内层空间
free(p);
p = NULL;
system("pause");
return EXIT_SUCCESS;
}
栈的存储特性:
局部变量:
形参:在定义函数时指定的形参,在未出现函数调用时,它们并不占内存中的存储单元,因此称它们是形式参数或虚拟参数,简称形参,表示它们并不是实际存在的数据,所以,形参里的变量不能赋值。
内存操作函数:
memset:
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
s:需要操作内存s的首地址
c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
n:指定需要设置的大小
返回值:s的首地址
示例
int a[10];
memset(a, 0, sizeof(a));
memset(a, 97, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%c\n", a[i]);
}
memcpy和memmove:
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
dest:目的内存首地址
src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错
n:需要拷贝的字节数
返回值:dest的首地址
示例
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10];
memcpy(b, a, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d, ", b[i]);
}
printf("\n");
//memcpy(&a[3], a, 5 * sizeof(int)); //err, 内存重叠
memmove
memmove()功能用法和memcpy()一样。
区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低些。
memcmp:
#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较s1和s2所指向内存区域的前n个字节
参数:
s1:内存首地址1
s2:内存首地址2
n:需比较的前n个字节
返回值:
相等:=0
大于:>0
小于:<0
示例
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int flag = *memcmp*(a, b, sizeof(a));
*printf*("flag = %d\n", flag);
内存常见问题:
1) 申请 0 字节空间
2)free空指针
3)越界访问
4)free ++后的地址
5)子函数malloc空间,main中用