【求非空字符串元素个数,求一串字符串里,不是’\n’的字符的个数】:
int no_space(char* str)
{
int count = 0;
char* p = str;
while (*p)
{
if (*p != ' ')
{
count++;
}
p++;
}
return count;
}
int main(int argc,char* argv[])
{
char str[] = "ni hao ma";
int count = no_space(str);
printf("%d\n", count);
}
【字符串逆置: 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--; // 尾元素对应指针前移
}
}
【判断字符串是回文】:
int converse(char* str)
{
char* start = str;
char* end = str + strlen(str) - 1;
while (start<end)
{
while (*start != *end) // 判断字符是否一致
{
return 0; // 表示不一致
}
start++;
end--; // 这里一定要是减法,不能是加法
}
return 1;
}
int main(int argc,char* argv[])
{
char* str = "oooo";
int a = converse(str);
printf("%s\n", a == 1 ? "是回文数字" : "不是回文数字");
}
1.字符串处理函数: #include <string.h>
1.1字符串拷贝: strcpy:
将 src 的内容,拷贝给 dest。 返回 dest。 保证dest空间足够大。【不安全】
char *strcpy(char *dest, const char *src);
函数调用结束 返回值和 dest参数结果一致。返回值是一个char *
char src[] = "hello";
char dest[100] = { 0 };
char *p = strcpy(dest, src); // src 拷贝给dest
printf("%s\n", p);
1.2 字符串拷贝 strncpy:
将 src 的内容,拷贝给 dest。只拷贝 n 个字节。 通常 n 与dest对应的空间一致。
默认 不添加 ‘\0’
char *strncpy(char *dest, const char *src, size_t n);
特性: n > src: 只拷贝 src 的大小
char src[] = "hello";
char dest[6] = { 0 };
char *p = strncpy(dest, src,6); // src 拷贝给dest
printf("%s\n", p);
1.3 字符串拼接: strcat
将 src 的内容,拼接到 dest 后。 返回拼接后的字符串。 保证 dest 空间足够大。
char *strcat(char *dest, const char *src);
char src[] = " world";
char dest[] = " hello";
char* p = strcat(dest, src); // src拼接在dest后面
printf("%s\n", p); // dest和p是一样的,所以要保证dest空间足够大
1.4 字符串拼接 strncat:
将 src 的前 n 个字符,拼接到 dest 后。 形成一个新的字符串。保证 dest 空间足够大(默认拼到dest里面了)。
char *strncat(char *dest, const char *src, size_t n);
函数调用结束 返回值和 dest 参数结果一致。
char src[] = " world";
char dest[] = " hello";
char* p = strncat(dest, src,3); // src的前n个字符拼接在dest后面 hello wo
printf("%s\n", p);
1.5 字符串比较: strcmp 不能使用 > < >= <= == !=
比较s1和s2两个字符串,如果相等 返回0.如果不相等,进一步表 s1 和 s2 对应位 ASCII码值。
s1 > s2 返回1
s1 < s2 返回-1
int strcmp(const char *s1, const char *s2);
只比较对应位,不比较总的ASCII值
char src[] = " b";
char dest[] = " a";
int a = strcmp(src,dest); //
printf("%d\n", a); // -1 a<b
1.6字符串比较 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
1.7 字符串格式化输入、输出:
1.7.1sprintf(): 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数组中。相比于printf多了一个str, 这一步不会输出,只有写入操作
1.7.2 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
1.8字符串查找字符、子串:
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); 正则表示式:可以接收除换行之外的所有字符
1.8字符串分割:
strtok(): 按照既定的分割符,来拆分字符串。“www.baidu.com” --> “www\0baidu.com”
char *strtok(char *str, const char *delim);
参1: 待拆分字符串
参2: 分割符组成的“分割串”,就是用什么来分割,以为www.baidu.com为例:strtok(“www.baidu.com”,“.”);
返回:字符串拆分后的首地址。 “拆分”:将分割字符用 '\0’替换。最后变成www baidu.com 两份
特性:
1)strtok拆分字符串是直接在 原串 上操作,所以要求参1必须,可读可写(char *str = “www.baidu.com” 不行!!!)
2)第一次拆分,参1 传待拆分的原串。 第1+ 次拆分时,参1传 NULL.
char str[] = "www.itcast.cn"; // 这个是变量,char *str是变量
int n = strlen(str);
char* p = strtok(str, "."); // 这里一定要是双引号,不然会报错
printf("p = %s\n", p);// 第一次拆分,参1传待拆分的原串
while (p != NULL)
{
p = strtok(NULL, "."); // 第n+1次拆分,参数传NULL
printf("p = %s\n", p);
}
for (size_t i = 0; i < n; i++)
{
printf("【%c】", str[i]);
}
/*
p = www
p = itcast
p = cn
p = (null)
【w】【w】【w】【】【i】【t】【c】【a】【s】【t】【】【c】【n】
*/
【 练习: 拆分 “.itcast.cn$This is a strtok$test”】
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);
}
1.9 atoi/atof/atol: a:字符串 i:整数 f: 浮点数 l:长整数
使用这类函数进行转换,要求,原串必须是可转换的字符串。
错误使用(不报错,强制转):(atoi)“abc123” --> 0; “12abc345” —> 12; “123xyz” -->123 转换的前提是字符串里面的内容是相应的格式,比如说整数,浮点数等等
atoi:字符串 转 整数。 int atoi(const char *nptr);
atof:字符串 转 浮点数
atol:字符串 转 长整数
char str1[] = "10";
int num1 = atoi(str1);
printf("num1 = %d\n", num1);
char str2[] = "0.123";
double num2 = atof(str2);
printf("num2 = %f\n", num2);
char str3[] = "123L";
long num3 = atol(str3);
printf("num3 = %ld\n", num3);
/*
num1 = 10
num2 = 0.123000
num3 = 123
*/
1.10 字符串处理函数的实现
(1) 回文字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int str_abcba(char *str)
{
char *start = str;
char *end = str + strlen(str) - 1;
while (start < end)
{
if (*start != *end)
{
return 0; // 不是回文。
}
start++;
end--;
}
return 1; // 是回文。
}
int main(void)
{
char str[] = "hello world";
int ret = str_abcba(str);
if (ret == 1)
{
printf("是回文字符串\n");
}
else
{
printf("不是回文字符串\n");
}
return EXIT_SUCCESS;
}
(2)字符串比较strcmp_strncmp
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int mystrcmp(const char* str1, const char*str2)
{
if (!str1 || !str2)
return -2;
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 > *str2 ? 1 : -1;
}
int mystrncmp(const char* str1, char* str2, size_t size)
{
if (!str1 || !str2)
return -2;
int i = 0;
while (*str1 == *str2)
{
if (i == size || *str1 == '\0')
return 0;
i++;
str1++;
str2++;
}
return *str1 > *str2 ? 1 : -1;
}
int main(void)
{
char str1[] = "hello world";
char str2[] = "hello kitty";
//int value = mystrcmp(str1, str2);
int value = mystrncmp(str1, str2, 5);
printf("%d\n", value);
if (value == 0)
{
printf("相同\n");
}
else
{
printf("不相同\n");
}
return EXIT_SUCCESS;
}
(3) 字符串拷贝strcpy_strncpy
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
char* mystrcpy(char* dest, const char* src)
{
char* temp = dest;
if (!dest || !src)
return NULL;
while (*dest++ = *src++);
return temp;
}
char* mystrncpy(char* dest, const char* src, size_t size)
{
if (!dest || !src)
return NULL;
size_t i;
for ( i = 0; i < size; i++)
{
*(dest+i) = *(src+i);
}
*(dest + i) = 0;
return dest;
}
int main(void)
{
char src[] = "hello world";
char dst[100];
//mystrcpy(dst, src);
mystrncpy(dst, src, 5);
printf("%s\n", dst);
return EXIT_SUCCESS;
}
(4)字符串逆置
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
//字符串逆置
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)
{
char str[] = "hello world";
str_inverse(str);
printf("%s\n", str);
return EXIT_SUCCESS;
}
(5) 字符串长度strlen
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
int my_strlen(const char *str)
{
char *p = str;
//while (*p != '\0')
while (*p)
{
p++;
}
return p-str;
}
int my_strlen2(const char *str)
{
char *p = str;
while (*p++);
return p - str -1;
}
int main(void)
{
char str[] = "hello world";
//int len = my_strlen(str);
int len = my_strlen2(str);
printf("len = %d\n", len);
system("pause");
return EXIT_SUCCESS;
}
(6)字符串拼接strcat_strncat
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
char* mystrcat(char* dst, const char* src)
{
char* temp = dst;
if (!dst || !src)
return NULL;
while (*dst)dst++;
while (*dst++ = *src++);
return temp;
}
char* mystrncat(char* dst, const char* src, size_t size)
{
char* temp = dst;
if (!dst || !src)
return NULL;
while (*dst)dst++;
int i;
for (i = 0; i < size; i++)
{
*(dst + i) = *(src + i);
}
*(dst + i) = 0;
return temp;
}
int main(void)
{
char src[] = "world";
char dst[100] = "hello";
//mystrcat(dst, src);
mystrncat(dst, src, 3);
printf("%s\n", dst);
return EXIT_SUCCESS;
}
(7)字符串找字符strchr
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
char *my_strchr(char *str, char ch)
{
while (*str)
{
if (*str == ch)
return str;
str++;
}
return NULL;
}
char *my_strchr2(char *str, char ch)
{
int i = 0;
//while (str[i] != '\0')
while (str[i]) // str[i] --> *(str+i)
{
if (str[i] == ch)
return &str[i];
i++;
}
return NULL;
}
int main(void)
{
char str[] = "hello";
char ch = 'l';
char *p = my_strchr2(str, ch);
if (p == NULL)
printf("没有\n");
else
printf("%s\n", p);
system("pause");
return EXIT_SUCCESS;
}
(8)字符串找子串strstr
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
char *my_strstr(char *str, char *substr)
{
char *pstr = str; // 遍历 str 的指针
char *temp = str; // 记录回滚位置的指针
char *psub = substr; // 遍历 substr 的指针
while (*pstr) // 遍历 原字符串。
{
temp = pstr; // 记录位置
while (*pstr == *psub && *pstr != '\0')
{
pstr++; // 一起向后走
psub++; // 一起向后走
}
if (*psub == '\0')
{
return temp;
}
// 回滚
psub = substr;
pstr = temp;
pstr++; // 向后走一个字符。
}
return NULL;
}
int main(void)
{
char str[] = "helhellolo world";
char substr[] = "hello";
printf("%s\n", my_strstr(str, substr));
system("pause");
return EXIT_SUCCESS;
}
2.变量及作用域
局部变量:
概念:定义在函数 内 部的变量。
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。
全局变量:
概念:定义在函数 外 部的变量。
作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。
extern a = 10; // 显式声明
static全局变量:
定义语法: 在全局变量定义之前添加 static 关键字。 static int a = 10;
作用域:被限制在本文件内部,不允许通过声明导出到其他文件。相对于全局变量,范围缩小了
static局部变量:
定义语法: 在局部变量定义之前添加 static 关键字。
特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。(相当于全局变量的功能,但是作用范围缩小了)
作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。
void test(void)
{
int b = 10;
printf("%d ", b++);
}
void test_static(void)
{
static int a = 100;
printf("%d ", a++);
}
int main(void)
{
//printf("%d", a); // 这里会报错,因为a是全局变量
printf("-----------------没有 static-------------------------\n");
for (size_t i = 0; i < 10; i++)
{
test();
}
printf("\n");
printf("--------------------有 static --------------------------\n");
for (size_t i = 0; i < 10; i++)
{
test_static();
}
/*
-----------------没有 static-------------------------
10 10 10 10 10 10 10 10 10 10
--------------------有 static --------------------------
100 101 102 103 104 105 106 107 108 109
*/
}
全局函数: 函数
定义语法: 函数原型 + 函数体
static函数:
定义语法:static + 函数原型 + 函数体
static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。
【生命周期】:作用域是在哪里起作用,生命周期是活多久
局部变量:从变量定义开始,函数调用完成。 — 函数内部。 栈帧上
全局变量:程序启动开始,程序终止结束。 — 程序执行期间。
static局部变量:程序启动开始,程序终止结束。 — 程序执行期间。
static全局变量:程序启动开始,程序终止结束。 — 程序执行期间。
全局函数:程序启动开始,程序终止结束。 — 程序执行期间。
static函数:程序启动开始,程序终止结束。 — 程序执行期间。
3.内存4区模型:
代码段:.text段。 程序源代码(二进制形式)。
数据段:只读数据段 .rodata段。初始化数据段 .data段。 未初始化数据段 .bss 段。
stack:栈。 在其之上开辟 栈帧。 windows 1M — 10M Linux: 8M — 16M
heap:堆。 给用户自定义数据提供空间。 约 1.3G+
【命名冲突:就近原则】 不推荐大家这样写
int b = 10;
int main(void)
{
int b = 100;
printf("%d\n", b); // 100
}
开辟释放 heap 空间:
void *malloc(size_t size); 申请 size 大小的空间
返回实际申请到的内存空间首地址。 【我们通常拿来当数组用】
void free(void *ptr); 释放申请的空间
参数: malloc返回的地址值。
使用 heap 空间:
空间时连续。 当成数组使用。
free后的空间,不会立即失效。 通常将free后的 地址置为NULL。
free 地址必须 是 malloc申请地址。否则出错。
如果malloc之后的地址一定会变化,那么使用临时变量tmp 保存。
// int arr[1000000000] = { 0,1,2 }; // 这里开始报错,因为超过了栈的容量,需要转移到堆中
int *p = (int *)malloc(sizeof(int) *10);
if (p == NULL)
{
printf("malloc error\n"); // 申请失败
return -1;
}
char * tmp = p; // 记录malloc的地址值,将来用于free操作
// 写数据到malloc空间
for (size_t i = 0; i < 10; i++)
{
p[i] = i + 10;
}
// 读出malloc空间中的数据
for (size_t i = 0; i < 10; i++)
{
printf("%d ",*(p+i) ); // 10 11 12 13 14 15 16 17 18 19
}
p++;
// 释放申请的内存
// free(p);
free(temp);
//p = NULL:
二级指针对应的 heap空间:
int** p = malloc(sizeof(int *)*3); //int **p ==> int *p[10]; ==> [int *, int *, int*]
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)
}
printf("\n");
}
// 释放空间,应该先释放内层空间
for (size_t i = 0; i < 3; i++)
{
free(p[i]); // *(p+i)
p[i] = NULL; // 这一句可有可无
}
// 释放外层空间
free(p);
p = NULL;
/*
01234
12345
23456
*/
申请外层指针: 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]);
}
释放外层z
free(p);