C语言学习之路(基础篇)—— 指针(下)

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

指针和函数

1) 函数形参改变实参的值

示例1:整型变量作为函数的形参,无法改变实参的值

#include <stdio.h>

void swap(int a, int b) {
    
    

	int c;
	c = a;
	a = b;
	b = c;
	printf("a=%d, b=%d\n", a, b);
	return;
}

int main() {
    
    

	int a = 10;
	int b = 20;
	swap(a, b);
	printf("a=%d, b=%d\n", a, b);


	return 0;
}
输出结果
a=20, b=10
a=10, b=20

示例2:指针变量作为函数的形参,可以改变实参的值

#include <stdio.h>

void swap(int a, int b) {
    
    

	int c;
	c = a;
	a = b;
	b = c;
	printf("a=%d, b=%d\n", a, b);
	return;
}
void swap2(int *x, int *y) {
    
    
	
	int c = *x;
	*x = *y;
	*y = c;
	printf("c=%d x=%d, y=%d\n", c, *x, *y);
	return;
}
int main() {
    
    

	int a = 10;
	int b = 20;
	//swap(a, b);
	swap2(&a, &b);
	printf("a=%d, b=%d\n", a, b);

	return 0;
}

在这里插入图片描述

输出结果
c=10 x=20, y=10
a=20, b=10

2) 数组名做函数参数

数组名做函数参数,函数的形参会退化为指针。

#include <stdio.h>

// 数组作为函数的形参会退化为指针
// 形参int b[10]就退化成int *b,哪怕形参是int b[10000],也是int *b
void printf_arr(int b[10]) {
    
    

	// 这里sizeof(b)==sizeof(int)==4个字节
	// sizeof(b[0])其中b[0] == int *(b+0) == int *b == a == 3,所以sizeof(b[0]) == sizeof(3) == sizeof(int)==4个字节
	int len = sizeof(b) / sizeof(b[0]); // 4/4=1
	for (int i = 0; i < len; i++)
	{
    
    
		printf("%d ", b[i]); // 3
	}


	return 0;
}

int main() {
    
    

	int a[10] = {
    
     3,9,5,1,4,7,6,10,2,8 };
	printf_arr(a); // 实参a为数组名,也就是首元素地址 &a[0], 类型为int*类型

	return 0;
}

那么就需要传递数组元素个数,让printf_arr函数知道,才能打印数组其他元素

#include <stdio.h>

// 数组作为函数的形参会退化为指针
// 形参int b[10]就退化成int *b,哪怕形参是int b[10000],也是int *b
void printf_arr(int b[10], int len) {
    
    

	// 这里sizeof(b)==sizeof(int)==4个字节
	// sizeof(b[0])其中b[0] == int *(b+0) == int *b == a == 3,所以sizeof(b[0]) == sizeof(3) == sizeof(int)==4个字节
	//int len = sizeof(b) / sizeof(b[0]); // 4/4=1
	for (int i = 0; i < len; i++)
	{
    
    
		printf("%d ", b[i]); // 3
	}


	return 0;
}

int main() {
    
    

	int a[10] = {
    
     3,9,5,1,4,7,6,10,2,8 };
	int len = sizeof(a) / sizeof(a[0]);
	printf_arr(a, len); // 实参a为数组名,也就是首元素地址 &a[0], 类型为int*类型

	return 0;
}

在这里插入图片描述

3) 指针做为函数的返回值

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int* getNum() {
    
    
	// srand()用来设置rand()产生随机数时的随机种子
	srand(time(0));
	int num = rand();
	return &num;

}

int main() {
    
    

	int* p = getNum();

	printf("%d\n", *p);


	return 0;
}

在这里插入图片描述

补充:在函数中定义的变量叫局部变量,局部变量一旦在函数结束之后就会被释放空间,所以在getNum函数return后,再去main函数中操作这块被释放空间是非法的,但是编译器不会告诉我们,这块空间没有被占用,所以不会出问题,但如果被占用,那么就会出错。

在函数外面定义的变量叫全局变量,整个工程都可以使用;整个变量程序启动开辟空间,直到程序结束才会释放空间。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// 在函数外面定义的变量叫全局变量,整个工程都可以使用
// 整个变量程序启动开辟空间,直到程序结束释放空间
int num = 0;

int* getNum() {
    
    
	// srand()用来设置rand()产生随机数时的随机种子
	srand(time(0));
	//{}中定义的变量叫局部变量,局部变量一旦在函数结束之后就会被释放空间
	//int num = rand();
	num = rand();
	return &num;

}

int main() {
    
    

	int* p = getNum();

	printf("%d\n", *p);


	return 0;
}

在这里插入图片描述

指针和字符串

1) 字符指针

示例1:

#include <stdio.h>

int main() {
    
    
	//指针与字符串
	char str[] = "cdtaogang"; //定义了一个字符数组,字符数组内容为cdtaogang\0
	//定义一个指针用来保存数组首元素的地址
	char* p = str;
	printf("%s\n", p); //%s打印一个字符串,要的是首个字符的地址
	printf("%s\n", p+2);
	printf("%c\n", *(p + 3));
	printf("%d\n", sizeof(str));
	printf("%d\n", sizeof(p));

	return 0;
}
输出结果
cdtaogang
taogang
a
10
4

示例2:

#include <stdio.h>

int main() {
    
    
	//指针与字符串
	char str[] = "cdtaogang"; //定义了一个字符数组,字符数组内容为cdtaogang\0
	//定义一个指针用来保存数组首元素的地址
	char* p = str;
	*p = 'N';
	p++;
	*p = 'B';
	p++;
	printf("%s\n", p);
	printf("%s\n", p-2);

	return 0;
}
输出结果
taogang
NBtaogang

2) 字符串常量

  • 字符串常量是不可以改变的,存在文字常量区
  • 文字常量区的内容是不可以改变的
  • char *p = "hello"; 代表将字符串常量的地址赋值给指针p

示例:

#include <stdio.h>
#include <string.h>

int main() {
    
    

    char  str[] = "helloworld";//定义了一个字符数组,字符数组内容为helloworld
                                                      //定义一个指针用来保存数组首元素的地址
    char* p = str;
    p = "abcdef";//字符串常量存文字常量区,""在使用时,取的是字符串首元素的地址
    printf("%s\n", p); // abcdef
    printf("%d\n", sizeof(p));//4
    printf("%d\n", sizeof("abcdef"));//7   abcdef\0
    printf("%d\n", strlen(p));//6
    printf("%d\n", strlen("abcdef"));//6  strlen返回字符串长度
    //文字常量区的内容是不可以改变的
    p = 'm';
    printf("%s\n", p); 

	return 0;
}
输出结果
abcdef
4
7
6
6

3) 字符指针做函数参数

示例1:

#include <stdio.h>
#include <string.h>

void mystrcat(char* dst, char* src)
{
    
    
	int n = strlen(dst);
	int i = 0;
	while (*(src + i)!=0)
	{
    
    
		*(dst+n) = *(src + i);
		i++;
		n++;
	}
	*(dst + n) = 0;
}

int main()
{
    
    
	char str1[128] = "hello ";
	char str2[128] = "cdtaogang";

	mystrcat(str1, str2); // 不用传递元素个数,因为字符数组最后一个元素为\0,可以以此作为判断
	printf("%d\n", strlen(str1));
	printf("%d\n", sizeof(str1));
	printf("str1 = %s", str1); 

	return 0;
}
输出结果
15
128
str1 = hello cdtaogang

示例2:

#include <stdio.h>

void mystrcat(char* dst, const char* src)
{
    
    
	int len1 = 0;
	int len2 = 0;
	// 遍历数组计算元素个数
	while (dst[len1])
	{
    
    
		len1++;
	}
	while (src[len2])
	{
    
    
		len2++;
	}

	int i;
	for (i = 0; i < len2; i++)
	{
    
    
		dst[len1 + i] = src[i];
	}
}

int main()
{
    
    
	char str1[100] = "hello ";
	char str2[] = "cdtaogang";

	mystrcat(str1, str2);
	printf("str1 = %s\n", str1);

	return 0;
}
输出结果
str1 = hello cdtaogang

4) const修饰的指针变量

  • const修饰一个变量为只读
  • const修饰指针,不能通过指针修改指针所指向的空间内容
  • const修饰指针变量,在初始化之后不能改变指针变量本身的指向
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    
    
	//const修饰一个变量为只读
	const int a = 10;
	//a = 100; //err

	//指针变量, 指针指向的内存, 2个不同概念
	char buf[] = "cdtaogang";

	//从左往右看,跳过类型,看修饰哪个字符
	//如果是*, 说明指针指向的内存不能改变
	//如果是指针变量,说明指针的指向不能改变,指针的值不能修改
	
	// const修饰指针,不能通过指针修改指针所指向的空间内容,但p变量本身的值是可以被修改的
	const char* p = buf; 
	// 等价于上面 char const *p1 = buf;
	//p[1] = '2'; //err // 不能通过p指针修改那块空间的内容
	p = "agdlsjaglkdsajgl"; //ok

	// const修饰指针变量p2初始化之后不能改变p2指针变量本身的指向
	char* const p2 = buf;
	p2[1] = '3';
	//p2 = "salkjgldsjaglk"; //err // 不能改变p2指针变量本身的指向

	// const修饰指针和指针变量,p3为只读,指向不能变,指向的内存也不能变
	const char* const p3 = buf;

	return 0;
}

5) 字符指针数组 *

字符指针数组:是一个数组,每一个元素都是字符指针。

示例1:

#include <stdio.h>
//字符指针数组
int main() {
    
    
	char* p1 = "hello";
	char* p2 = "hi";
	char* p3 = "hey";
	//char* str[3] = { p1, p2,p3 };
	char* str[3] = {
    
     "hello", "hi","hey" };
	for (int i = 0; i < 3; i++)
	{
    
    	
		printf("%s ", str[i]);
	}
	printf("\n");
	// 打印首元素的值
	printf("%c\n", *str[0]);
	// 打印字符串hi中的i
	printf("%c\n", *(str[1] + 1));
	// 打印字符串hey中的y
	printf("%c\n", *(str[2] + 2));
	// 打印字符指针数组所有元素中的e字符
	for (int i = 0; i < sizeof(str); i++)
	{
    
    	
		for (int j = 0; j < strlen(str[i]); j++)
		{
    
    
			if (str[i][j] == 'e')
			{
    
    
				printf("%c\n", str[i][j]);
			}
		}
	}
	return 0;
}

在这里插入图片描述

输出结果
hello hi hey
h
i
y
e
e

示例2:

#include <stdio.h>

int main() {
    
    

	char* str[3] = {
    
     "hello", "hi","hey" };
	//定义一个指针保存str数组首元素的地址
	char** p = str;  // &str[0] == str

	for (int i = 0; i < 3; i++)
	{
    
    
		printf("%s", *(p+i));
		printf("%s\n", p[i]);
	}

	return 0;
}

在这里插入图片描述

输出结果
hellohello
hihi
heyhey

示例3:

#include <stdio.h>

int main() {
    
    

	char* str[3] = {
    
     "hello", "hi","hey" };
	//定义一个指针保存str数组首元素的地址
	char** p = str;  // &str[0] == str
	
	// 通过指针p取出首元素的值
	printf("%c", *(*(p + 0))); // *(p+0)得到str[0]里面的内容0x1000,而0x1000又代表hello\0首元素地址,所以取首元素的值就是*(*(p+0))
	printf("%c", *(p[0]+0)); // 上一步简写
	printf("%c", p[0][0]); // 再简写
	// 通过指针p取出数组元素hey中的e字符
	printf("%c", *(*(p + 2) + 1)); // *(p+2)得到str[2]里面的内容0x3000,而0x3000又代表hey\0首元素地址,在首元素地址上+1即*(p+2)+1就得到e字符的地址,所以取e字符就是*(*(p+2)+1)
	printf("%c", *(p[2] + 1));
	printf("%c", p[2][1]);
	return 0;
}

在这里插入图片描述

6) 指针数组做为main函数的形参

int main(int argc, char *argv[]);
  • main函数是操作系统调用的,第一个参数标明argc数组的成员数量,argv数组的每个成员都是char *类型
  • argv是命令行参数的字符指针数组,保存的是参数(字符串)的首元素地址
  • argc代表命令行参数的数量,程序名字本身算一个参数
#include <stdio.h>

//argc: 传参数的个数(包含可执行程序)
//argv:指针数组,指向输入的参数
// .*.exe   hello  123456
int main(int argc, char* argv[])
{
    
    
	// 打印运行参数
	printf("%d\n", argc);
	printf("%s\n", argv[0]);
	printf("%s\n", argv[1]);
	printf("%s\n", argv[2]);
	printf("%s\n", argv[3]);
	return 0;
}

右击项目>属性,配置调试命令参数 hello 123456

在这里插入图片描述

相当于char* argv[] = {".*.exe", "hello", "123456"},传参不够后,打印取值为null

在这里插入图片描述

通过argc的值,就能知道传参个数,所以可以通过for循环来打印输入的参数;需要注意的是在命令行参数之前还有.*.exe程序路径

#include <stdio.h>

//argc: 传参数的个数(包含可执行程序)
//argv:指针数组,指向输入的参数
// .*.exe   hello  123456
// char* argv[] = {".*.exe", "hello", "123456"}
int main(int argc, char* argv[])
{
    
    
	for (int i = 0; i <argc; i++)
	{
    
    
		printf("%s\n", argv[i]);
	}
	return 0;
}
输出结果
E:\VisualStudioProjects\StudyCProject\Debug\StudyCProject.exe
hello
123456

我们可以通过gcc命令编译生成exe可执行程序来执行命令行参数的打印

#include <stdio.h>
// 形参名随便取
int main(int n, char *v[]){
    
    
	
	for(int i = 0; i < n; i++)
	{
    
    
		printf("%s\n", v[i]);
	}
	
	return 0;
}
PS C:\Users\Administrator\Desktop> gcc hello.c -o hello
PS C:\Users\Administrator\Desktop> ./hello.exe hello CSDN cdtaogang good good study

在这里插入图片描述

7) 字符串处理函数

7.1 strcpy()

拷贝字符串:

  • 表头文件:#include <string.h>
  • 定义函数:char *strcpy(char *dest, const char *src);
  • 功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
  • 参数:
    dest:目的字符串首地址
    src:源字符首地址
  • 返回值:
    成功:返回dest字符串的首地址
    失败:NULL

注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strcpy函数
	char str1[128] = "";
	char str2[128] = "cdtaogang";
	char str3[128] = "hello\0cdtaogang";
	char str4[128] = "csdn";
	strcpy(str1, str2);
	printf("%s\n", str1); // cdtaogang
	strcpy(str1, str3); // 遇到 '\0'就结束拷贝
	printf("%s\n", str1); // hello
	strcpy(str2, str4); // csdn后面的'\0'也会拷贝过去,遇到'\0'结束掉相当于替换掉原始数据
	printf("%s\n", str2); // csdn
	return 0;
}
输出结果
cdtaogang
hello
csdn

7.2 strncpy()

拷贝字符串:

  • 表头文件:#include <string.h>
  • 定义函数:char *strncpy(char *dest, const char *src, size_t n);
  • 功能:把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'
  • 参数:
    dest:目的字符串首地址
    src:源字符首地址
    n:指定需要拷贝字符串个数
  • 返回值:
    成功:返回dest字符串的首地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strncpy函数
	char str1[128] = "";
	char str2[128] = "hellocdtaogang";
	char str3[128] = "he\0llocdtaogang";

	strncpy(str1, str2, 2);
	printf("str1=%s\n", str1); // he

	strncpy(str1, str2, 5);
	printf("str1=%s\n", str1); // hello

	strncpy(str1, str3, 5);
	printf("str1=%s\n", str1); // he
	// 验证将将str3字符串前5个字符拷贝至str1中是 he\0\0\0 还是 he\0wl
	for (int i = 0; i < 5; i++)
	{
    
    
		printf("%d ", str1[i]); // 104 101 0 0 0 说明拷贝的情况是不足n个字符就遇到了\0那么后面就不拷贝了
	}
	return 0;
}
输出结果
str1=he
str1=hello
str1=he
104 101 0 0 0

7.3 strcat()

连接两字符串:

  • 表头文件:#include <string.h>
  • 定义函数:char *strcat(char *dest, const char *src);
  • 功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
  • 参数:
    dest:目的字符串首地址
    src:源字符首地址
  • 返回值:
    成功:返回dest字符串的首地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
   // strcat函数
   char str1[128] = "hello";
   char str2[128] = "cdtaogang";
   char str3[128] = "world\0ocdtaogang";

   strcat(str1, str2);
   printf("str1=%s\n", str1); // hellocdtaogang

   strcat(str1, str3);
   printf("str1=%s\n", str1); // hellocdtaogangworld

   return 0;
}
输出结果
str1=hellocdtaogang
str1=hellocdtaogangworld

7.4 strncat()

连接两字符串:

  • 表头文件:#include <string.h>
  • 定义函数:char *strncat(char *dest, const char *src, size_t n);
  • 功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
  • 参数:
    dest:目的字符串首地址
    src:源字符首地址
    n:指定需要追加字符串个数
  • 返回值:
    成功:返回dest字符串的首地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strncat函数
	char str1[128] = "hello";
	char str2[128] = "123456";
	char str3[128] = "world\0ocdtaogang";

	strncat(str1, str2, 3);
	printf("str1=%s\n", str1); // hello123

	strncat(str1, str3, 10);
	printf("str1=%s\n", str1); // hello123world

	return 0;
}
输出结果
str1=hello123
str1=hello123world

7.5 strcmp()

比较字符串:

  • 表头文件:#include <string.h>
  • 定义函数:int strcmp(const char *s1, const char *s2);
  • 功能:比较 s1s2 的大小,比较的是字符ASCII码大小。
  • 参数:
    s1:字符串1首地址
    s2:字符串2首地址
  • 返回值:
    相等:0
    大于:>0 在不同操作系统strcmp结果会不同 返回ASCII差值
    小于:<0

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strcmp函数
	char* str1 = "hello world";
	char* str2 = "hello cdtaogang";
	char* str3 = "hello cdtaogang";
	char* str4 = "zzzzzzzzz";

	printf("strcmp(str1, str2):%d\n", strcmp(str1, str2)); // 1
	printf("strcmp(str2, str3):%d\n", strcmp(str2, str3)); // 0
	printf("strcmp(str2, str4):%d\n", strcmp(str2, str4)); // -1

	if (strcmp(str1, str2) == 0)
	{
    
    
		printf("str1==str2\n");
	}
	else if (strcmp(str1, str2) > 0)
	{
    
    
		printf("str1>str2\n");
	}
	else  // < 0
	{
    
    
		printf("str1<str2\n");
	}

	return 0;
}
输出结果
strcmp(str1, str2):1
strcmp(str2, str3):0
strcmp(str2, str4):-1
str1>str2

7.6 strncmp()

比较字符串:

  • 表头文件:#include <string.h>
  • 定义函数:int strncmp(const char *s1, const char *s2, size_t n);
  • 功能:比较 s1s2n个字符的大小,比较的是字符ASCII码大小。
  • 参数:
    s1:字符串1首地址
    s2:字符串2首地址
    n:指定比较字符串的数量
  • 返回值:
    相等:0
    大于:>0
    小于:<0

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strncmp函数
	char* str1 = "hello world";
	char* str2 = "hello cdtaogang";
	char* str3 = "hello cdtaogang";
	char* str4 = "zzzzzzzzz";

	printf("strncmp(str1, str2, 7):%d\n", strncmp(str1, str2, 7)); // 1
	printf("strncmp(str2, str3, 6):%d\n", strncmp(str2, str3, 6)); // 0
	printf("strncmp(str2, str4, 1):%d\n", strncmp(str2, str4, 1)); // -1

	if (strncmp(str1, str2, 8) == 0)
	{
    
    
		printf("str1==str2\n");
	}
	else if (strncmp(str1, str2, 8) > 0)
	{
    
    
		printf("str1>str2\n");
	}
	else  // < 0
	{
    
    
		printf("str1<str2\n");
	}

	return 0;
}
输出结果
strncmp(str1, str2, 7):1
strncmp(str2, str3, 6):0
strncmp(str2, str4, 1):-1
str1>str2

7.7 sprintf()

格式化字符串复制(组包函数)

  • 表头文件:#include <stdio.h>
  • 定义函数:int sprintf(char *str, const char *format, ...);
  • 功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 '\0' 为止。
  • 参数:
    str:字符串首地址
    format:字符串格式,用法和printf()一样
  • 返回值:
    成功:实际格式化的字符个数
    失败: -1

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// sprintf函数
	int year = 2022;
	int month = 11;
	int day = 19;

	//printf("today is %d-%d-%d\n", year, month, day);

	char buf[1024] = "";
	char buf2[1024] = "";
	sprintf(buf, "today is %d-%d-%d\n", year, month, day); // 将数据安装格式组包,存放在数组buf中,sprintf函数的返回值是组完包的有效长度
	printf("buf=[%s]\n", buf); // buf=[today is 2022-11-19
                               //]
	// 打印buf字符个数
	int len2 = sprintf(buf2, "to%cday is %d-%d-%d\n", 0, year, month, day);
	printf("%d\n", strlen(buf2)); // 2  strlen遇到0就结束了,所以字符个数不准确
	printf("len=%d\n", len2); // len=21
	return 0;
}
输出结果
buf=[today is 2022-11-19
]
2
len=21

7.8 sscanf()

格式化字符串输入(拆包函数)

  • 表头文件:#include <stdio.h>
  • 定义函数:int sscanf(const char *str, const char *format, ...);
  • 功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
  • 参数:
    str:指定的字符串首地址
    format:字符串格式,用法和scanf()一样
  • 返回值:
    成功:参数数目,成功转换的值的个数
    失败: -1

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// sscanf函数
	int year;
	int month;
	int day;

	//scanf("%d-%d-%d", &year, &month, &day);// 从键盘按照相应的格式获取数据
	char buf[1024] = "beijing:2022-11-19";
	sscanf(buf, "beijing:%d-%d-%d", &year, &month, &day); // 从buf中按照相应的格式获取数据
	printf("today is %d-%d-%d", year, month, day);

	return 0;
}
输出结果
today is 2022-11-19

7.9 strchr()

查找字符串中第一个出现的指定字符

  • 表头文件:#include <string.h>
  • 定义函数:char *strchr(const char *s, int c);
  • 功能:在字符串s中查找字母c出现的位置
  • 参数:
    s:字符串首地址
    c:匹配字母(字符)
  • 返回值:
    成功:返回第一次出现的c地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// 编写代码实现第一次出现a字符的地址
	char src[] = "ddda123abcd";
	int i = 0;
	char* p = src;
	while (p[i]!=0)
	{
    
    
		if (p[i] == 'a')
		{
    
    
			p = &p[i];
			break;
		}
		i++;
	}
	printf("p = %s\n", p);

	return 0;
}

示例2:

char *my_strchr(char* p, char ch) {
    
    

	int i = 0;
	while (p[i]!=0)
	{
    
    
		if (p[i]==ch)
		{
    
    
			return &p[i];
		}
		i++;
	}
	return NULL;
}
int main() {
    
    
	// 定义函数实现
	char src[] = "ddda123abcd";
	char* p = my_strchr(src, 'a');
	printf("p = %s\n", p);

	return 0;
}

示例3:

int main() {
    
    
	// strchr函数
	char src[] = "ddda123abcd";
	char* p = strchr(src, 'a');
	printf("p = %s\n", p);

	return 0;
}

在这里插入图片描述

7.10 strstr()

在一字符串中查找指定的字符串

  • 表头文件:#include <string.h>
  • 定义函数:char *strstr(const char *haystack, const char *needle);
  • 功能:在字符串haystack中查找字符串needle出现的位置
  • 参数:
    haystack:源字符串首地址
    needle:匹配字符串首地址
  • 返回值:
    成功:返回第一次出现的needle地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

char* my_strstr(char* str1, char *str2) {
    
    

	int i = 0;
	// str[i] == *(str+i)
	while (str1[i] != 0)
	{
    
    
		if (str1[i] == str2[0])
		{
    
    	// str1+i就表示str1中'a'的地址
			if (0==strncmp(str1+i, str2, strlen(str2)))
			{
    
    
				return str1+i;
			}
		}
		i++;
	}
	return NULL;
}
int main() {
    
    
	// 定义函数实现
	char str1[] = "ddddabcd123abcd333abcd";
	char str2[] = "abcd";
	char* p = my_strstr(str1, str2);
	printf("p = %s\n", p);
	return 0;
}

示例2:

int main() {
    
    
	// strstr函数
	char str1[] = "ddddabcd123abcd333abcd";
	char str2[] = "abcd";
	char* p = strstr(str1, str2);
	printf("p = %s\n", p);
	return 0;
}
输出结果
p = abcd123abcd333abcd

7.11 strtok()

分割字符串

  • 表头文件:#include <string.h>
  • 定义函数:char *strtok(char *str, const char *delim);
  • 功能:来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0
  • 参数:
    str:指向欲分割的字符串
    delim:为分割字符串中包含的所有字符
  • 返回值:
    成功:分割后字符串首地址
    失败:NULL

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    
	// strtok函数
	char str[] = "CSDN#1126331350#cdtaogang#9528";
	char* p1 = strtok(str, "#");
	printf("p1=%s\n", p1); // p1=CSDN
	// strtok会记录每次切割,并将切割部分内部记录为\0,所以第二次及之后的切割从NULL开始往后找
	char* p2 = strtok(NULL, "#");
	printf("p2=%s\n", p2); // p2=1126331350
	char* p3 = strtok(NULL, "#");
	printf("p3=%s\n", p3); // p3=cdtaogang
	char* p4 = strtok(NULL, "#");
	printf("p4=%s\n", p4); // p4=9528
	
	return 0;
}

示例2:

int main() {
    
    
	// strtok函数
	char str[] = "CSDN#1126331350#cdtaogang#9528";
	// 使用while循环将"#"分割的子串全部取出
	char* s = strtok(str, "#");
	while (s != NULL)
	{
    
    
		printf("%s\n", s);
		s = strtok(NULL, "#");
	}
	return 0;
}

示例3:

int main() {
    
    
	// strtok函数
	char str[] = "CSDN#1126331350#cdtaogang#9528";
	// 使用do while循环将"#"分割的子串全部取出
	char* p[10] = {
    
     NULL };
	int i = 0;
	do
	{
    
    
		if (i == 0) // 表示第一次切割
		{
    
    
			p[i] = strtok(str, "#");
		}
		else
		{
    
    
			p[i] = strtok(NULL, "#");
		}
	} while (p[i++]!=NULL); // i在前先使用再++,即p[0]!=NULL i=i+1 如果strtok的返回值等于NULL,代表切割完毕
	// 循环打印
	i = 0;
	while (p[i++]!=NULL)
	{
    
    
		printf("%s\n", p[i-1]); // 打印时i已经+1了

	}
	return 0;
}

示例4:

int main() {
    
    
	// strtok函数
	char str[] = "CSDN#1126&331350#cdtaogang#95&28";
	// 使用do while循环将"#&"分割的子串全部取出
	char* p[10] = {
    
     NULL };
	int i = 0;
	do
	{
    
    
		if (i == 0) // 表示第一次切割
		{
    
    
			p[i] = strtok(str, "#&");
		}
		else
		{
    
    
			p[i] = strtok(NULL, "#&");
		}
	} while (p[i++]!=NULL); // i在前先使用再++,即p[0]!=NULL i=i+1 如果strtok的返回值等于NULL,代表切割完毕
	// 循环打印
	i = 0;
	while (p[i++]!=NULL)
	{
    
    
		printf("%s\n", p[i-1]); // 打印时i已经+1了
	}
	return 0;
}

在这里插入图片描述

7.12 atoi()

将字符串转换为整型数

  • 表头文件:#include <stdlib.h>
  • 定义函数:int atoi(const char *nptr);
  • 功能:atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符('\0')才结束转换,并将结果返回返回值。
  • 参数:
    nptr:待转换的字符串
  • 返回值:成功转换后整数

类似的函数有:

  • atof():把一个小数形式的字符串转化为一个浮点数。
  • atol():将一个字符串转化为long类型

示例1:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

int main() {
    
    
	// atoi函数
	// 0-9的字符和-+号就开始转,如果不是+-0-9的字符就结束,如果前面有空格,就跳过
	char a[] = "1234";
	char b[] = "-1234";
	char c[] = "e1234";
	char d[] = " 1234";
	printf("a=%d\n", atoi(a));
	printf("b=%d\n", atoi(b));
	printf("c=%d\n", atoi(c));
	printf("d=%d\n", atoi(d));
	// atof函数
	char e[] = "3.14";
	printf("e=%f\n", atof(e));
	// atol函数
	char f[] = "11321321";
	printf("f=%ld\n", atol(f));
	return 0;
}
输出结果
a=1234
b=-1234
c=0
d=1234
e=3.140000
f=11321321

8) 项目开发常用字符串应用模型

8.1 strstr中的while和do-while模型

利用strstr标准库函数找出一个字符串中abcd出现的次数。

while模型

#include <stdio.h>
#include <string.h>


int main() {
    
    

	char* p = "32e23abcd11132122abcd333abcd9922abcd333abc32d2qqq";
	int n = 0;
	// 查找匹配到到则进去,循环条件第一次从字符串首元素地址开始查找
	while ((p=strstr(p, "abcd"))!=NULL)
	{
    
    	
		// 进入循环后,说明此时已经匹配到一个了,那么就要重新设置查找起点位置
		p += strlen("abcd");
		n++;
	}
	printf("n=%d", n);
	return 0;
}
输出结果
n=4

do-while模型

int main() {
    
    

	char* p = "32e23abcd11132122abcd333abcd9922abcd333abc32d2qqq";
	int n = 0;
	do
	{
    
    
		p = strstr(p, "abcd");
		if (p!=NULL)
		{
    
    	
			p += strlen("abcd");
			n++;
		}
		
	} while (p!=NULL);
	printf("n=%d", n);
	return 0;
}
输出结果
n=4

8.2 两头堵模型

求非空字符串元素的个数:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

int main() {
    
    

	char str[] = "    hello cdtaogang     ";//hello cdtaogang
    char buf[128] = "";
	// 判断空字符
    if (str[0] == 0)
		return;
    char * begin = str; // 数组首元素地址,也就是起始位置
	// str字符串的长度-1就表示最后一个元素的下标,取最后一个字符的地址
    char *end = &str[strlen(str) - 1];//end指向最后一个字符
    //从起始位置开始找,找到第一个不是空格的位置,并且不能移出数组最后一个位置\0
    while (*begin == ' ' &&  *begin != 0)
    {
    
    
		begin++;
    }
	//从结束位置开始找,找到第一个不是空格的位置,并且不能等于begin字符串的起始位置
    while (*end == ' '  &&  end != begin)
    {
    
    
            end--;
    }
	// 打印数组的长度,字符结束位置-起始位置+1得到字符串的长度
    printf("%d\n",end-begin +1);
	// 将匹配出来的字符串拷贝到buf数组中
    strncpy(buf, begin, end-begin +1);
    printf("buf=%s\n", buf);
    system("pause");

	return 0;
}
输出结果
15
buf=hello cdtaogang

8.3 字符串反转模型(逆置)

在这里插入图片描述

#include <stdio.h>


int main() {
    
    

	char str[] = "abcdef";

	char* begin = str;
	char* end = &str[strlen(str) - 1];
	printf("%c %c\n", *begin, *end);  // a f
	printf("str=%s\n", str);
	while (end > begin)
	{
    
    	
		//交换元素
		char ch = *begin;
		*begin = *end;
		*end = ch;
		begin++;
		end--;
	}
	printf("str=%s\n", str);

	return 0;
}

在这里插入图片描述

指针小结

定义 说明
int i 定义整形变量
int *p 定义一个指向int的指针变量
int a[10] 定义一个有10个元素的数组,每个元素类型为int
int *p[10] 定义一个有10个元素的数组,每个元素类型为int*
int func() 定义一个函数,返回值为int型
int *func() 定义一个函数,返回值为int *型
int **p 定义一个指向int的指针的指针,二级指针(即p指向的地址里面存放的是一个指向int的一级指针)

猜你喜欢

转载自blog.csdn.net/qq_41782425/article/details/127894938