字符串
字符串的基本操作
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
//一级指针的典型用法
//字符串
//1 C语言的字符串 以零'\0'结尾的字符串
//2 在C语言中没有字符串类型 通过字符数组 来模拟字符串
//3 字符串的内存分配 堆上 栈上 全局区
//字符数组 初始化
int main()
{
//1 不指定长度 C编译器会自动帮程序员 求元素的个数
char buf1[] = {'a', 'b', 'c', 'd'}; //buf1是一个数组 不是一个以0结尾的字符串
//2 指定长度
char buf2[100] = {'a', 'b', 'c', 'd'};
//后面的buf2[4]-buf2[99] 个字节均默认填充0
//char buf3[2] = {'a', 'b', 'c', 'd'};
//如果初始化的个数大于内存的个数 编译错误
printf("buf1: %s\n", buf1);
printf("buf2: %s \n", buf2);
printf("buf2[88]:%d \n", buf2[88]);
//3 用字符串初始化 字符数组
char buf3[] = "abcd"; //buf3 作为字符数组 有5个字节
// 作为字符串有 4个字节
int len = strlen(buf3);
printf("buf3字符的长度:%d \n", len); //4
//buf3 作为数组 数组是一种数据类型 本质(固定小大内存块的别名)
int size = sizeof(buf3); //
printf("buf3数组所占内存空间大小:%d \n", size); //5
char buf4[128] = "abcd"; // buf
printf("buf4[100]:%d \n", buf4[100]);//0
return 0;
}
sizeof 和 strlen的区别
1.sizeof为一个操作符,执行sizeof的结果,在编译期间就已经确定;
strlen是一个函数,是在程序执行的时候才确定结果。
2. sizeof和strlen对于求字符串来讲,sizeof() 字符串类型的大小,
包括’\0’;strlen() 字符串的长度不包括‘\0’(数字 0 和字符‘\0’等价)。
数组法和指针法操作字符串
#include <stdio.h>
#include <string.h>
int main(void)
{
int i = 0;
char *p = NULL;
char buf[128] = "abcdefg";
//通过[]
for (i=0; i<strlen(buf); i++)
{
printf("%c ", buf[i]);
}
p = buf; //buf 代表数组首元素的地址
//通过指针
for (i=0; i<strlen(buf); i++)
{
printf("%c ", *(p+i) ) ;
}
//通过数组首元素地址 buf 来操作
for (i=0; i<strlen(buf); i++)
{
printf("%c ", *(buf+i) ) ;
}
return 0;
}
• [] 的本质 和 *p 是一样。
buf 是一个指针,只读的常量。
之所以buf是一个常量指针是为了释放内存的时候,保证buf所指向的内存空间安全。
练习题:
画出字符串一级指针内存四区模型
#include <stdio.h>
#include <string.h>
int main(void)
{
char buf[20]= "aaaa";//栈里
char buf2[] = "bbbb"; //栈里
char *p1 = "111111"; //常量区
char *p2 = malloc(100); //堆
strcpy(p2, "3333");//把数据拷贝到堆里
return 0;
}
字符串做函数参数
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
char a[] = "i am a student";
char b[64];
int i = 0;
for (i=0; *(a+i) != '\0'; i++)
{
*(b+i) = *(a+i);
}
//0没有copy到b的buf中.
b[i] = '\0'; //重要
printf("a:%s \n", a);
printf("b:%s \n", b);
return 0;
}
以上是拷贝一个字符串用到了数组名(也就是数组首元素地址)的偏移来完成。
项目开发常用字符串应用模型
strstr中的while和do-while模型
利用strstr标准库函数找出一个字符串中substr出现的个数。
do-while模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
//strstr(str, str2)
int ncount = 0;
//初始化 让p指针达到查找的条件
char *p = "11abcd111122abcd3333322abcd3333322qqq";
do
{
p = strstr(p, "abcd");
if (p != NULL)
{
ncount++; //
p = p + strlen("abcd"); //指针达到下次查找的条件
}
else
{
break;
}
} while (*p != '\0');
printf("ncount:%d \n", ncount);
return 0;
}
while模型
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void)
{
int ncount = 0;
//初始化 让p指针达到查找的条件
char *p = "2abcd3333322qqqabcd";
while ( (p = strstr(p, "abcd")) != NULL )
{
ncount ++;
p = p + strlen("abcd"); //让p指针达到查找的条件
if (*p == '\0')
{
break;
}
}
printf("ncount:%d \n", ncount);
return 0;
}
练习:
求字符串p中 abcd出现的次数:
1 请自定义函数接口,完成上述需求
2 自定义的业务函数 和 main函数必须分开
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void fun(char *p,int *count)
{
int ncount = 0;
while ( (p = strstr(p, "abcd")) != NULL )
{
ncount ++;
p = p + strlen("abcd"); //让p指针达到查找的条件
if (*p == '\0')
{
break;
}
}
*count = ncount;
}
int main(void)
{
int ncount = 0;
char p[] = "2abcd3333322qqqabcd";
fun(p,&ncount);
printf("%d\n",ncount);
system("pause");
return 0;
}
两头堵模型
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int get_count_non_space(char *str, int * count_p)
{
int i = 0;
int j = strlen(str)-1;
int ncount = 0;
if (str == NULL || count_p == NULL) {
fprintf(stderr, "str == NULL, count_p == NULL\n");
return -1;
}
while (isspace(str[i]) && str[i] != '\0')
{
i++;
}
while (isspace(str[j]) && j > i)
{
j--;
}
ncount = j-i+1;
*count_p = ncount;
return 0;
}
int main(void)
{
char *p = " abcdefg ";
int ret = 0;
int ncount = 0;
ret = get_count_non_space(p, &ncount);
if (ret < 0) {
fprintf(stderr, "get_count_non_space err, ret = %d\n", ret);
return 0;
}
printf("ncount = %d\n", ncount);
return 0;
}
练习:
有一个字符串开头或结尾含有n个空格 (” abcdefgdddd ”),
欲去掉前后空格,返回一个新字符串。
要求1:请自己定义一个接口(函数),并实现功能;
要求2:编写测试用例。
int trimSpace(char *inbuf, char *outbuf);
int trimSpace(char *inbuf, char *outbuf)
{
if(inbuf==NULL)
{
return 0;
}
int i = 0;
int j = strlen(inbuf)-1;
int ncount = 0;
if (inbuf == NULL ) {
fprintf(stderr, "str == NULL, count_p == NULL\n");
return -1;
}
while (isspace(inbuf[i]) && inbuf[i] != '\0')
{
i++;
}
while (isspace(inbuf[j]) && j > i)
{
j--;
}
ncount = j-i+1;
strncpy(outbuf,inbuf+i,ncount);
outbuf[ncount] = '\0';
return 0;
}
int main(void)
{
char inbuf[30] = " abcdefgdddd ";
char outbuf[30];
trimSpace(inbuf,outbuf);
printf("%s",outbuf);
system("pause");
return 0;
}
字符串反转模型
char* inverse(char *str)
{
int length = strlen(str);
char *p1 = NULL;
char *p2 = NULL;
char tmp_ch;
if (str == NULL) {
return NULL;
}
p1 = str;
p2 = str + length -1;
while (p1 < p2) {
tmp_ch = *p1;
*p1 = *p2;
*p2 = tmp_ch;
++p1;
--p2;
}
return str;
}
越界
char buf[3] = “abc”;
记得反斜杠0;
谈谈const
• const 知识点
一. const声明的变量只能被读
const int i=5;
int j=0;
i=j; //非法,导致编译错误
j=i; //合法
二.必须初始化
const int i=5; //合法
const int j; //非法,导致编译错误
三.如何在另一.c源文件中引用const常量
extern const int i; //合法
extern const int j=10; //非法,常量不可以被再次赋值
四.可以避免不必要的内存分配
/*
由于const定义常量从汇编的角度来看,只是给出了对应的内存地址,
而不是象#define一样给出的是立即数,所以,const定义的常量在
程序运行过程中只有一份拷贝,而#define定义的常量在内存中有
若干个拷贝。
*/
五.C语言中const是一个冒牌货
const char *c; // 是一个指向常整型数的指针
// (所指向的内存数据不能修改,
// 但是本身可以修改)
char * const d; // 常指针(指针变量不能被修改,
// 但是它所指向的内存空间可以被修改)
const char * const e;// 一个指向常量整型的常指针
// (指针和它所指向的内存空间,
// 均不可以修改)
const好处
合理的利用const,
1 指针做函数参数,可以有效的提高代码可读性,减少bug;
2 清楚的分清参数的输入和输出特性
要求写一个函数实现如下功能:
功能1:把偶数位字符挑选出来,组成一个字符串1。
功能2:把奇数位字符挑选出来,组成一个字符串2。
功能3:把字符串1和字符串2,通过函数参数,传送给main,并打印。
功能4:主函数能测试通过。
int getStr1Str2(char *source, char *buf1, char *buf2);
int getStr1Str2(char *source, char *buf1, char *buf2)
{
if(source==NULL)
{
return -1;
}
int i = 0;
int j = 0,k = 0;
for(i = 0;i<strlen(source);i++)
{
if(i%2==0)
{
buf1[j] = source[i];
j++;
}
else
{
buf2[k] = source[i];
k++;
}
}
buf1[k++] = '\0';
buf2[j++] = '\0';
return 0;
}