10W+字C语言硬核总结(六),值得阅读收藏!

1.4.2 字符串的格式化


1.4.2.1 sprintf


#include <stdio.h>

int sprintf(char *str, const char *format, ...);

功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到 出现字符串结束符 '\0' 为止。


参数:


str:字符串首地址


format:字符串格式,用法和printf()一样


返回值:


成功:实际格式化的字符个数


失败: - 1

void test(){
 
 //1. 格式化字符串
 char buf[1024] = { 0 };
 sprintf(buf, "你好,%s,欢迎加入我们!", "John");
 printf("buf:%s\n",buf);
 
 memset(buf, 0, 1024);
 sprintf(buf, "我今年%d岁了!", 20);
 printf("buf:%s\n", buf);
 
 //2. 拼接字符串
 memset(buf, 0, 1024);
 char str1[] = "hello";
 char str2[] = "world";
 int len = sprintf(buf,"%s %s",str1,str2);
 printf("buf:%s len:%d\n", buf,len);
 
 //3. 数字转字符串
 memset(buf, 0, 1024);
 int num = 100;
 sprintf(buf, "%d", num);
 printf("buf:%s\n", buf);
 //设置宽度 右对齐
 memset(buf, 0, 1024);
 sprintf(buf, "%8d", num);
 printf("buf:%s\n", buf);
 //设置宽度 左对齐
 memset(buf, 0, 1024);
 sprintf(buf, "%-8d", num);
 printf("buf:%s\n", buf);
 //转成16进制字符串 小写
 memset(buf, 0, 1024);
 sprintf(buf, "0x%x", num);
 printf("buf:%s\n", buf);
 
 //转成8进制字符串
 memset(buf, 0, 1024);
 sprintf(buf, "0%o", num);
 printf("buf:%s\n", buf);
}

1.4.2.2 sscanf


#include <stdio.h>

int sscanf(const char *str, const char *format, ...);

功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。


参数:


str:指定的字符串首地址


format:字符串格式,用法和scanf()一样


返回值:


成功:成功则返回参数数目,失败则返回-1


失败: - 1

//1. 跳过数据
void test01(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 //匹配第一个字符是否是数字,如果是,则跳过
 //如果不是则停止匹配
 sscanf("123456aaaa", "%*d%s", buf); 
 printf("buf:%s\n",buf);
}
 
//2. 读取指定宽度数据
void test02(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 sscanf("123456aaaa", "%7s", buf);
 printf("buf:%s\n", buf);
}
 
//3. 匹配a-z中任意字符
void test03(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 //先匹配第一个字符,判断字符是否是a-z中的字符,如果是匹配
 //如果不是停止匹配
 sscanf("abcdefg123456", "%[a-z]", buf);
 printf("buf:%s\n", buf);
}
 
//4. 匹配aBc中的任何一个
void test04(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 //先匹配第一个字符是否是aBc中的一个,如果是,则匹配,如果不是则停止匹配
 sscanf("abcdefg123456", "%[aBc]", buf);
 printf("buf:%s\n", buf);
}
 
//5. 匹配非a的任意字符
void test05(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 //先匹配第一个字符是否是aBc中的一个,如果是,则匹配,如果不是则停止匹配
 sscanf("bcdefag123456", "%[^a]", buf);
 printf("buf:%s\n", buf);
}
 
//6. 匹配非a-z中的任意字符
void test06(){
 char buf[1024] = { 0 };
 //跳过前面的数字
 //先匹配第一个字符是否是aBc中的一个,如果是,则匹配,如果不是则停止匹配
 sscanf("123456ABCDbcdefag", "%[^a-z]", buf);
 printf("buf:%s\n", buf);
}


1.5 一级指针易错点


1.5.1 越界


void test(){
 char buf[3] = "abc";
 printf("buf:%s\n",buf);
}

1.5.2 指针叠加会不断改变指针指向


void test(){
 char *p = (char *)malloc(50);
 char buf[] = "abcdef";
 int n = strlen(buf);
 int i = 0;
 for (i = 0; i < n; i++)
 {
  *p = buf[i];
  p++; //修改原指针指向
 }
 free(p);
}


1.5.3 返回局部变量地址


char *get_str()
{
 char str[] = "abcdedsgads"; //栈区,
 printf("[get_str]str = %s\n", str);
 return str;
}


1.5.4 同一块内存释放多次(不可以释放野指针)


void test(){ 
 char *p = NULL;
 p = (char *)malloc(50);
 strcpy(p, "abcdef");
 if (p != NULL)
 {
  //free()函数的功能只是告诉系统 p 指向的内存可以回收了
  // 就是说,p 指向的内存使用权交还给系统
  //但是,p的值还是原来的值(野指针),p还是指向原来的内存
  free(p); 
 }
 if (p != NULL)
 {
  free(p);
 }
}

1.6 const使用

//const修饰变量
void test01(){
 //1. const基本概念
 const int i = 0;
 //i = 100; //错误,只读变量初始化之后不能修改
 
 //2. 定义const变量最好初始化
 const int j;
 //j = 100; //错误,不能再次赋值
 
 //3. c语言的const是一个只读变量,并不是一个常量,可通过指针间接修改
 const int k = 10;
 //k = 100; //错误,不可直接修改,我们可通过指针间接修改
 printf("k:%d\n", k);
 int* p = &k;
 *p = 100;
 printf("k:%d\n", k);
}
 
//const 修饰指针
void test02(){
 
 int a = 10;
 int b = 20;
 //const放在*号左侧 修饰p_a指针指向的内存空间不能修改,但可修改指针的指向
 const int* p_a = &a;
 //*p_a = 100; //不可修改指针指向的内存空间
 p_a = &b; //可修改指针的指向
 
 //const放在*号的右侧, 修饰指针的指向不能修改,但是可修改指针指向的内存空间
 int* const p_b = &a;
 //p_b = &b; //不可修改指针的指向
 *p_b = 100; //可修改指针指向的内存空间
 
 //指针的指向和指针指向的内存空间都不能修改
 const int* const p_c = &a;
}
//const指针用法
struct Person{
 char name[64];
 int id;
 int age;
 int score;
};
 
//每次都对对象进行拷贝,效率低,应该用指针
void printPersonByValue(struct Person person){
 printf("Name:%s\n", person.name);
 printf("Name:%d\n", person.id);
 printf("Name:%d\n", person.age);
 printf("Name:%d\n", person.score);
}
 
//但是用指针会有副作用,可能会不小心修改原数据
void printPersonByPointer(const struct Person *person){
 printf("Name:%s\n", person->name);
 printf("Name:%d\n", person->id);
 printf("Name:%d\n", person->age);
 printf("Name:%d\n", person->score);
}
void test03(){
 struct Person p = { "Obama", 1101, 23, 87 };
 //printPersonByValue(p);
 printPersonByPointer(&p);
}


2. 指针的指针(二级指针)


2.1 二级指针基本概念


这里让我们花点时间来看一个例子,揭开这个即将开始的序幕。考虑下面这些声明:


int a = 12;

int *b = &a;

它们如下图进行内存分配:

10W+字C语言硬核总结(六),值得阅读收藏!

假定我们又有了第3个变量,名叫c,并用下面这条语句对它进行初始化:


c = &b;

它在内存中的大概模样大致如下:

10W+字C语言硬核总结(六),值得阅读收藏!


c的类型是什么?显然它是一个指针,但它所指向的是什么?


变量b是一个“指向整型的指针”,所以任何指向b的类型必须是指向“指向整型的指针”的指针,更通俗地说,是一个指针的指针。


它合法吗?


是的!指针变量和其他变量一样,占据内存中某个特定的位置,所以用&操作符取得它的地址是合法的。


那么这个变量的声明是怎样的声明的呢?


int **c = &b;

那么这个**c如何理解呢?操作符具有从右想做的结合性,所以这个表达式相当于(*c),我们从里向外逐层求职。*c访问c所指向的位置,我们知道这是变量b.第二个间接访问操作符访问这个位置所指向的地址,也就是变量a.指针的指针并不难懂,只需要留心所有的箭头,如果表达式中出现了间接访问操作符,你就要随箭头访问它所指向的位置。


上一篇:Android 标签栏pagerslidingtabstrip用法实例(含Demo)


下一篇:尝试通过uniapp仿微信页面