字符串或串(String)是由数字、字母、下划线组成的一串字符。一般记为 s=“a1a2···an”(n>=0)。它是编程语言中表示文本的数据类型。在程序设计中,字符串(string)为符号或数值的一个连续序列,如符号串(一串字符)或二进制数。
补充:字符串在存储上类似字符数组,它每一位单个元素都是能提取的。
定义字符串:
编程练习:如何定义字符串
#include <stdio.h>
int main()
{
//字符串既然叫做字符数组,就可以用定义数组的方法来定义它
/*int a[5] = {12,13,14,15,16};
for(int i=0;i<5;i++) 之前定义数组及其遍历方法
{
printf("%d ",a[i]);
}*/
char cdata[5] = {'h','e','l','l','o'}; //打印时候最后可能有乱码,后面解决
//cdata2作为字符串变量(指针常量)可以用来修改里边的数值
//pchar作为字符串常量(常量指针),不可以修改里边存放的值
// 字符串变量
char cdata2[] = "hello"; //前边的定义方式繁琐,采用后两种
// 字符串常量,不允许修改。
char *pchar = "hello";
char c = 'c'; //定义一个字符的方法
printf("%s",cdata2);//字符串打印
putchar('\n') ; //输出一个字符
printf("%s",pchar);//字符串打印
putchar('\n') ; //输出一个字符
puts(pchar); //输出多个字符
// 修改字符串
// *pchar = 'L'; 不能修改
cdata2[2] = 'L';
printf("%s",cdata2);
/*
for(int i=0;i<5;i++)
{
printf("%c",cdata2[i]);
//printf("%c\n",*(pchar+i)); //遍历方式较为繁琐
}*/
/*char *p; //*p是一个野指针,对野指针的赋值会发生段错误
*p = 'm';
puts("done\n");*/
return 0;
}
注意:指针可以保存地址、修改指向、指向字符常量的地址空间;但操作野指针的地址空间是不可行的,会造成段错误!!!
常量指针本质上是一个指针,指针就是地址,它所指向的地址可变,但里边存放的值不可变;指针常量本质上是一个常量,它所指向的地址不可变,一旦定义就确定了它的地址,但里边存放的常量可以被修改。
存储方式:
字符串的存储方式:会在字符串的末尾加一个'\0'
代码验证:用sizeof计算字符串的长度
#include <stdio.h>
int main()
{
//字符串的结束标志:'\0'
//实际上字符在内存空间中的存储方式
char cdata[] = {'h','e','l','l','0','\0'}; //字符串的结束标志'\0' ,当用定义字符数组时,要注意在最后补'\0'
char cdata2[] = "hello"; //定义字符串时会自动在后边补'\0'
char *pchar = "hello";
int len1 = sizeof(cdata2)/sizeof(cdata2[0]);
int len2 = sizeof(cdata)/sizeof(cdata[0]);
int len3 = sizeof(pchar)/sizeof(pchar[0]);
printf("len1 = %d\n",len1);
printf("len2 = %d\n",len2);
printf("len3 = %d\n",len3); //最后一个是表示OS操作系统用8字节来存放一个地址
printf("%s\n",cdata); //当没有'\0'的结束标志时,输出字符串就可能有乱码
return 0;
}
strlen和sizeof的区别:
strlen是计算字符串的有效长度(不包含'\0'),sizeof是计算整个字符串的长度
编程验证:
#include <stdio.h>
#include <string.h>
void My_str()
{
}
int main()
{
char str[] = "hello";
char str2[128] = "hello";
char *p = "hello";
void (*p2)();
p2 = My_str;
printf("strlen of str: %d\n",strlen(str)); // 5 //计算字符串的有效长度,不包含'\0'
printf("sizeof of str: %d\n",sizeof(str)); // 6 //计算整个字符串的长度
printf("strlen of str2: %d\n",strlen(str2)); //5 //计算字符串的有效长度,不包含'\0'
printf("sizeof of str2: %d\n",sizeof(str2)); //128 //计算整个字符串的长度
printf("strlen of p: %d\n",strlen(p)); //5 //计算字符串的有效长度,不包含'\0'
printf("sizeof of p: %d\n",sizeof(p)); //8 //以下都是指针变量,OS操作系统用8字节来存放一个地址
printf("sizeof of int *: %d\n",sizeof(int *));//8
printf("sizeof of char *: %d\n",sizeof(char *));//8
printf("sizeof of p2: %d\n",sizeof(p2));//8
return 0;
}
动态开辟字符串
C 库函数 - malloc() 开辟内存空间,p有了固定的地址,不再是野指针
描述
C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针。
声明
void *malloc(size_t size)
参数:
- size_t int 型
- size -- 内存块的大小,以字节为单位。
返回值
该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL。
C 库函数 - calloc() 开辟内存空间
描述
C 库函数 void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
声明
void *calloc(size_t nitems, size_t size)
参数
- nitems -- 要被分配的元素个数。
- size -- 元素的大小。
返回值
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
C 库函数 - strcpy() 拷贝strcpy 将新数据放置到目标文件
描述
C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。
声明
void *memset(void *str, int c, size_t n)
参数
- str -- 指向要填充的内存块。
- c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
- n -- 要被设置为该值的字符数。
返回值
该值返回一个指向存储区 str 的指针。
C 库函数 - realloc() 扩容函数字符大小
描述
C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小。
声明
void *realloc (void *ptr, size_t size)
参数
- ptr -- 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针。
- size -- 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针。
返回值
该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL。
memset
编程练习:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/*char *p ; //野指针
p ='a';
puts("end") // 这里是野指针 puts语句就不会被执行*/
char *p;
p = (char *)malloc(1); //malloc开辟内存空间,p有了固定的地址,不再是野指针
*p = 'c';
free(p); //防止堆空间被耗尽,将malloc/calloc分配的空间释放掉
p = NULL; // 如果空间释放后,没有新的内存空间,就需要将 指针赋值为空 ,防止成为野指针。
p = (char *)malloc(12);
if (p == NULL){
printf("malloc error\n");
exit(-1);
}
memset(p,'\0',12); // 通过memset函数将12个字符清理,初始成\0;
// 目标地址 , 新文件
strcpy(p,"liuxianglong"); // 将新数据放置到目标文件
printf("扩容地址 : %x\n" ,p);
int len = strlen("liuliuxianglong1234546317789654"); //计算字符串有多少字符
int newlen = len - 12 + 1; //(+1 是给\0留个位置)
realloc(p,newlen);// realloc 扩容函数字符
printf("扩容地址 : %x\n" ,p);
strcpy(p,"liuxianglong1234546317789654"); //12个字符,放不下这么多字节 拷贝strcpy
printf("%c\n",*p); //输出(1)
printf("%s\n",p); //输出(12)
puts(p); //输出(12)
puts("end");
return 0;
}
字符串输出 printf
#include <stdio.h>
// 输出字符串
int main()
{
char *p = "liuxiaglong";
printf("请输出字符串\n");
puts(p); //puts函数自带换行符
return 0 ;
}
获取字符串:scanf
#include <stdio.h>
// 输出字符串
int main()
{
char *p = "liuxiaglong";
printf("请输出字符串\n");
puts(p); //puts函数自带换行符
char str[128] = {'\0'};
printf("请输入字符串\n");
scanf("%s",str); //字符串输出
puts(str);
return 0;
}
获取字符串 gets
#include <stdio.h>
// 输出字符串
int main()
{
char *p = "liuxiaglong";
printf("请输出字符串\n");
puts(p); //puts函数自带换行符
char str1[128] = {'\0'};
printf("请输入字符串2\n");
//scanf("%s",str); //字符串输出
gets(str1);
puts(str1);
return 0;
}
注意:sanf 和gets不能同时出现在一个模块函数内!
gets用于从标准输入中读取字符串,
scanf用于从标准输入中读取格式化输入,包括整数、浮点数等。
计算字符有效长度
编程说明:计算有效字符的长度:strlen(不包括'\0')
#include <stdio.h>
#include <string.h>
// 获取字符串的有效长度
int main()
{
char str[128] = "hello world";
// 获取字符串的有效长度
int len1 = strlen(str);
printf("len1 = %d\n",len1);
// 获取字符串的全部长度
int len2 = sizeof(str);
printf("len2 = %d\n",len2);
return 0;
}
字符串拷贝
编程说明:字符串拷贝:strcpy 和 strncpy的区别
/*
用strcpy实现字符串的全部拷贝 strncpy函数是部分拷贝
*/
//char *strcpy(char *dest, const char *src)
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = {'\0'};
char *p = "makabaka handsome";
strcpy(str,p); //stpcpy函数是全部拷贝
puts(str);
return 0;
}
/*
用strncpy实现字符串的拷贝 可指定字符串的字符长度
*/
//char *strncpy(char *dest, const char *src, size_t n)
#include <stdio.h>
#include <string.h>
int main()
{
char str[128] = {'\0'};
char *p = "makabaka handsome";
strncpy(str,p,8); // 指定拷贝8个字符到目标函数中去
puts(str);
return 0;
}
由此可见:strcpy 和 strncpy 的区别是
strncpy可以指定要拷贝多少个字符到目标函数中去,
strcpy只可以拷贝所有的字符到目标函数中。
#include <stdio.h>
//strcpy 的原型函数
// 两个参数 形参,原字符
char* myStrcpy(char *des, char *src)
{
//if的作用是现计算表达式 形参,如果其值为假(即为0),
if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
while( *src != '\0'){
*des = *src; //先赋值
// 在偏移
des++;
src++;
}
*des = '\0'; // 结束标准
return bak;
}
// 两个参数 形参,原字符
char* myStrcpy2(char *des, char *src)
{
if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
while( *src != '\0'){
*des++ = *src++; //先赋值,在偏移
}
*des = '\0'; // 结束标准
return bak;
}
char* myStrcpy3(char *des, char *src)
{
if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
//先赋值, 在循环
while( (*des++ = *src++) != '\0');
*des = '\0'; // 结束标准
return bak;
}
int main()
{
char str[128] = {'\0'};
char a = 'm';
char b ;
if ((b = a )== 'm'){ //测试能否先赋值 ,在进行if判断
printf("ok\n");
}
char *p = "liuxiaglong handsome";
// myStrcpy(str,p);//stpcpy函数是全部拷贝
myStrcpy3(str,p);//stpcpy函数是全部拷贝
puts(str);
return 0;
}
#include <stdio.h>
//strncpy 的原型函数
// 三个参数 形参,原字符 ,字符数
char* myStrncpy(char *des, char *src,int count)
{
//if的作用是现计算表达式 形参,如果其值为假(即为0),
if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
while( *src != '\0' && count >0){
*des = *src; //先赋值
// 在偏移
des++;
src++;
count--;
}
// 如果count 设定成了20个字符,原字符只有15个, *src = '\0'的情况下,将其他没有的字符将输出成'\0';
if(count >0){
while(count > 0){
count -- ;
* des = '\0';
}
}
*des = '\0'; // 结束标准
return bak;
}
int main()
{
char str[128] = {'\0'};
char a = 'm';
char b ;
if ((b = a )== 'm'){ //测试能否先赋值 ,在进行if判断
printf("ok\n");
}
char *p = "liuxiaglong handsome";
myStrncpy(str,p,5);//stpcpy函数是拷贝指定多少个字符
puts(str);
return 0;
}
#include <stdio.h>
//strncpy 的原型函数
// 三个参数 形参,原字符 ,字符数
char* myStrncpy(char *des, char *src,int count)
{
//if的作用是现计算表达式 形参,如果其值为假(即为0),
if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
while( *src != '\0' && count >0){
*des = *src; //先赋值
// 在偏移
des++;
src++;
count--;
}
/*
如果count 设定成了25个字符,原字符只有10个,
*src = '\0'的情况下,将其他没有的字符将输出成'\0'; */
if(count >0){
while(count > 0){
count -- ;
* des++ = '\0';
printf("ok\n"); //这里的ok将会输出4次。
}
return des ;
}
*des = '\0'; // 结束标准
return bak;
}
int main()
{
char str[128] = {'\0'};
char *p = "liuxiaglong handsome";
myStrncpy(str,p,25);//stpcpy函数是拷贝指定多少个字符
puts(str);
return 0;
}
断言(assert)
assert() 的用法像是一种"契约式编程",其表达的意思就是,程序在假设条件下,能够正常良好的运作,其实就相当于一个 if 语句。
但是这样写的话,就会有无数个 if 语句,甚至会出现,一个 if 语句的括号从文件头到文件尾,并且大多数情况下,我们要进行验证的假设,只是属于偶然性事件,又或者我们仅仅想测试一下,一些最坏情况是否发生,所以这里有了 assert()。
assert 宏的原型定义在 assert.h 中,其作用是如果它的条件返回错误,则终止程序执行。
assert 的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向 stderr 打印一条出错信息,然后通过调用 abort 来终止程序运行。(第二个代码)
编程说明:断言(assert)函数的运用
#include <stdio.h>
#include <assert.h>
//assert 断链的原型函数
// 2个参数 形参,原字符 ,字符数
char* myStrcpy(char *des, char *src)
{
*if(des == NULL || src == NULL){
return NULL;
}
char *bak = des; // char一个备份地址,去保存目标地址,因为需要进行赋值。
while( *src != '\0' ){
*des = *src; //先赋值
// 在偏移
des++;
src++;
}
*des = '\0'; // 添加字符串终止符
return bak;
}
int main()
{
char str[128] = {'\0'};
char *p = "liuxiaglong handsome";
myStrcpy(str,p);
puts(str);
return 0;
}
当字符串指针为空时, assert函数启用
断言 输出 :
如果 pdes 和src为空时 程序结束,并且提示des != NULL && src != NULL 这句话
#include <stdio.h>
#include <assert.h>
//strcpy 的原型函数
// 2个参数 形参,原字符 ,
char* myStrcpy(char *pdes, char *src)
{
//if的作用是现计算表达式 形参,如果其值为假(即为0),
assert(pdes != NULL && src != NULL);
/*if(des == NULL || src == NULL){
return NULL;
}*/
char *bak = pdes; // char一个备份地址,去保存目标地址,因为需要进行赋值。
// 将源字符串拼接到目标字符串的末尾
while( *src != '\0' ){
*pdes = *src; //先赋值
// 在偏移
pdes++;
src++;
}
// 添加字符串终止符
*pdes = '\0'; // 结束标准
return bak;
}
int main()
{
char str[128] = {'\0'};
char *pstr = NULL;
char *p = "liuxiaglong handsome";
myStrcpy(pstr,p);
puts(str);
return 0;
}
字符串拼接
编程说明:字符串拼接 strcat
#include <stdio.h>
/*
用strcat实现字符串的拼接
*/
#include <string.h>
int main()
{
// 无返回值 ,一般情况下不需要返回值
char str[] = "hello";
char *p = "World";
strcat(str,p);
puts(str);
return 0;
}
strcat_有返回值
#include <stdio.h>
/*
用strcat实现字符串的拼接
*/
#include <string.h>
int main()
{
// 无返回值 ,一般情况下不需要返回值
char str[] = "hello";
char *p = "World";
strcat(str,p);
puts(str);
printf("----------\n");
//有返回值
char str1[] = "hello1";
char *p1 = "World1";
char *p2;
p2 = strcat(str1,p1);
puts(p2);
return 0;
}
自定义函数实现字符串拼接
#include <stdio.h>
/*
用strcat实现字符串的拼接
*/
#include <string.h>
// 自定义函数实现字符串拼接,参数为目标字符串des和源字符串src
char* myStrcat(char *des, char *src)
{
char *bak = des; // 定义一个指针,拷贝des
// 查找目标字符串的末尾
while(*des !='\0'){
des++; //如果末尾的不是\0 就++
}
// 将源字符串拼接到目标字符串的末尾
while((*des++ = *src++) != '\0');
// 添加字符串终止符
*des = '\0';
return bak; // 返回拼接后的字符串的起始地址
}
int main()
{
// 无返回值 ,一般情况下不需要返回值
char str[] = "hello";
char *p = "World";
strcat(str,p); // 将p拼接到str的末尾
puts(str);// 打印拼接后的字符串
printf("----------\n");
//有返回值
// 使用自定义函数进行字符串拼接
char str1[] = "hello2";
char *p1 = "World2";
char *p2;
p2 = myStrcat(str1,p1); 调用自定义函数拼接字符串
puts(p2); // 打印拼接后的字符串
return 0;
}
字符串比较
编程说明: 字符串比较:strcmp 和 strncmp 的区别
strcmp比较整个字符串,而strncmp比较指定的前n个字符。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char *p1 = "happyn";
char *p2 = "happym";
// strcmp 函数,p1和p2进行比较 p1小 输出-1 ,p1大 输出1,相对输出0;
int ret =strcmp(p1,p2);
if(ret == 0){
printf("p1 and p2 是相等的");
}
printf("RET = %d\n",ret);
return 0;
}
/*
strncmp的功能:比较指定字符串的大小
*/
//int strncmp ( const char * str1, const char * str2, size_t n )
#include <stdio.h>
#include <string.h>
int main()
{
/*
功能是把 str1 和 str2 进行比较,最多比较前 n 个字节,
若str1与str2的前n个字符相同,则返回0;
若s1大于s2,则返回大于0的值;
若s1 小于s2,则返回小于0的值。
*/
char *p1 = "happywworld";
char *p2 = "happymworld";
int ret =strncmp(p1,p2,7);
if(ret == 0){
printf("p1 and p2 是相等的");
}
printf("n = %d\n",ret);
return 0;
}