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
int sscanf(const char *str, const char *format, ...);
功能:從str指定的字元串讀取資料,并根據參數format字元串來轉換并格式化資料。
str:指定的字元串首位址
format:字元串格式,用法和scanf()一樣
成功:成功則傳回參數數目,失敗則傳回-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;
它們如下圖進行記憶體配置設定:

假定我們又有了第3個變量,名叫c,并用下面這條語句對它進行初始化:
c = &b;
它在記憶體中的大概模樣大緻如下:
c的類型是什麼?顯然它是一個指針,但它所指向的是什麼?
變量b是一個“指向整型的指針”,是以任何指向b的類型必須是指向“指向整型的指針”的指針,更通俗地說,是一個指針的指針。
它合法嗎?
是的!指針變量和其他變量一樣,占據記憶體中某個特定的位置,是以用&操作符取得它的位址是合法的。
那麼這個變量的聲明是怎樣的聲明的呢?
int **c = &b;
那麼這個**c如何了解呢?操作符具有從右想做的結合性,是以這個表達式相當于(*c),我們從裡向外逐層求職。*c通路c所指向的位置,我們知道這是變量b.第二個間接通路操作符通路這個位置所指向的位址,也就是變量a.指針的指針并不難懂,隻需要留心所有的箭頭,如果表達式中出現了間接通路操作符,你就要随箭頭通路它所指向的位置。