常用函數
一、字符串函數
1、字符串在程序中如何保存使用?
char A[10] = “hello”; -> 將常量區"hello"拷貝到數組A內存空間。
char *p = “hello”; -> 將常量區"hello"首元素的地址保存在指針變量p中。
2、在linux下,有些關於字符串函數提供用戶使用。
1)計算字符串實際長度 ->strlen()
2)比較兩個字符串是否匹配 -> strcmp()
3)拷貝字符串到某段內存中 -> strcpy()
4)追加字符串 ->strcat()
1、計算字符串實際長度 strlen( )-> 不包含’\0’在內
Ubuntu:man 3 strlen
NAME -> 函數的功能
strlen - calculate the length of a string -> 校對字符串長度
#include <string.h> -> 頭文件
size_t strlen(const char *s); -> 函數原型
s: 需要計算的字符串的首元素的地址
返回值:字符的個數。
例子:
#include <stdio.h>
#include <string.h>
int main()
{
char A[100] = "helloworld";
char *p = "helloworld";
printf("sizeof(A) = %d\n",sizeof(A));//100
printf("sizeof(p) = %d\n",sizeof(p));//4
printf("strlen(A) = %d\n",strlen(A));//10
printf("strlen(p) = %d\n",strlen(p));//10
return 0;
}
結論: sizeof()計算空間大小,strlen()計算字符串的長度。
2、比較兩個字符串是否匹配 -> strcmp()
1、與整型變量是否相等比較
int a = 200;
int b = 100;
if(a == b) -> 整型數據使用"=="
來進行判斷
2、比較兩個字符串能不能用"=="?
char A[100] = “helloworld”;
char *p = “helloworld”;
if(A == p) -> 不可以,只能判斷兩個地址是否相等,這樣對比是沒有任何意義,只有對比內容纔有意義。
3、 如何比較兩個字符串? -> 只能使用strcmp() -> man 3 strcmp
NAME
strcmp, strncmp - compare two strings 比較兩個字符串
#include <string.h>
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
s1
:需要進行比較的字符串1的地址
s2
:需要進行比較的字符串2的地址
n
: 只匹配前n個字節
返回值:
s1
與s2
匹配 -> 0
s1
與s2
不匹配 -> 非0
4、例子:
#include <stdio.h>
#include <string.h>
int main()
{
int ret;
char A[100] = "helloworld";
char *p = "hello";
ret = strncmp(A,p);
if(ret == 0){
printf("match!\n");
}
else{
printf("no match!\n");
}
return 0;
}
練習:有一張銀行卡,密碼是gec123456,請求用戶輸入密碼,如果密碼正確,則輸出密碼的長度,如果密碼錯誤,則重新輸入!如果輸入的次數超過5次,則程序直接退出。
#include <stdio.h
#include <string.h>
int main(int argc,char *argv[])
{
char passwd[20] = "gec123456"; //銀行卡密碼
char input_passwd[20] = {0}; //清空數組
char *p = input_passwd;
int count = 0;
while(count < 5)
{
scanf("%s",p);
if(strcmp(passwd,p) == 0)
{
printf("passwd length = %d\n",strlen(passwd));
break;
}
else{
count++;
continue;
}
}
return 0;
}
3、拷貝字符串到某段內存中 -> strcpy()
char A[10] = “hello”;
char A[10] = {“hello”};
char A[10] = {‘h’,‘e’,‘l’,‘l’,‘o’};
char A[10]; -> 如果定義沒有初始化,則以後都不能整體初始化。
A[0] = ‘h’;
…
strcpy()
-> 如果定義沒有初始化,則以後還能使用strcpy()整體初始化。 -> man 3 strcpy
char A[10];
A = “hello” //錯誤
strcpy(A,“hello”); //正確
NAME
strcpy, strncpy - copy a string
SYNOPSIS
#include <string.h>
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
dest:需要把字符串拷貝到的空間的起始地址,這個空間必須足夠大。
src:需要拷貝的字符串的地址。
n: 只拷貝src前n個字節
返回值:
成功: 返回指向dest內存空間的地址
失敗: NULL
#include <stdio.h>
#include <string.h>
int main()
{
char A[11] = "helloworld";
char *p = "abc";
int i;
for(i=0;i<11;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
strcpy(A,p);
for(i=0;i<11;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
printf("A = %s\n",A);
return 0;
}
4、字符串函數 -> 追加字符串 strcat()
字符串函數 -> 追加字符串strcat()
-> man 3 strcat
使用格式:
#include <string.h>
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
src
:需要追加到另一個字符串默認的字符串的地址。 -> 拷貝之後會覆蓋dest的\0。
dest
:被追加的字符串的空間,空間必須足夠大。
返回值:指向dest區域的首元素的地址。
驗證: overwriting the terminating null byte (’\0’) at the end of dest, and then adds a terminating null byte.
src覆蓋dest的\0,並且會在拼接之後在字符串的默認添加一個\0。
#include <stdio.h>
#include <string.h>
int main()
{
/* 可以 */
char A[20] = "hello";
strcat(A,"world");
/* 可以 */
char A[20] = "hello";
char *p = A;
strcat(p,"world");
/* 不可以 */
char *p = "hello";
strcat(p,"world");
printf("p = %s\n",p); //helloworld
return 0;
}
總結:學習過4個字符串函數的使用場景
- strlen() -> 確定某些數據的字節數 -> 文件IO/系統編程/網絡編程 write()/send()
- strcmp() -> 檢索特徵數據 -> 鏈表/網絡編程 -> 對比特徵值/協議是否一致
- strcpy() -> 用於初始化字符數組
char A[10]="helloworld"; 對
char A[10];
A = "helloworld"; 不對
char A[10];
strcpy(A,"hello");
- strcat -> 拼接字符串,僅限追加功能 -> 後期使用sprintf() 替代 strcat()
5、 數組清零方式
1、定義數組的同時初始化0
例子: char str[50] = {0};
特點:只能初始化(清零)一次。
2、清空某段內存空間。 -> bzero() -> man 3 bzero
bzero - write zero-valued bytes -> 函數的功能
#include <strings.h> -> 頭文件
void bzero(void *s, size_t n); -> 函數原型
s:需要清零的內存的地址
n:需要清零的字節數
返回值:無
特點:多次調用,多次清零。
#include <stdio.h>
#include <strings.h>
int main()
{
char A[10];
int i;
for(i=0;i<10;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
bzero(A,sizeof(A));
for(i=0;i<10;i++)
{
printf("A[%d] = %c\n",i,A[i]);
}
return 0;
}
二、堆空間
1、堆空間的特點:主動申請,主動釋放
2、如何申請堆空間? -> malloc() -> man 3 malloc
#include <stdlib.h>
void *malloc(size_t size);
size:需要申請的字節數
The memory is not initialized. -> 內存沒有被初始化過。
返回值:
成功: 指向堆空間的起始地址
失敗: NULL
例子:
void *pa = malloc(4) -> 在堆空間申請4個字節,然後在棧空間申請一個指針變量pa指向堆空間。
int *p = pa; -> 把堆空間的pa賦值賦值給指針變量p
int pa; -> 在棧空間申請4個字節
int *p = &pa; -> 把棧空間pa變量的地址賦值給指針變量p
例題1: 申請了堆空間,堆空間的值會是什麼?
#include <stdio.h>
#include <stdlib.h>
int main()
{
int A[3];
printf("A = %p\n",A);
int *p = malloc(sizeof(int)*3); // int A[3];
printf("p = %p\n",p);
printf("p[0] = %d\n",p[0]); //0
printf("p[1] = %d\n",p[1]); //0
printf("p[2] = %d\n",p[2]); //0
free(p);
return 0;
}
3、如何釋放空間? -> free() -> man 3 free
#include <stdlib.h>
void free(void *ptr);
ptr:需要釋放堆空間的地址
返回值:無
一般做法:free掉指向堆區指針之後,會讓指針指向NULL。
free§;
p = NULL;
三、堆空間與棧空間在內存中容易出錯的點
下列代碼是否正確?如果正確,請指出代碼的含義,如果出錯,則說明錯誤的原因。
1、棧區
char A[10] = “hello”; //把常量區的hello拷貝到變量A數組中
strcpy(A,“world”); //把常量區的world拷貝到變量A數組中
============================
char *p = “hello”; //把常量區的hello的首元素的地址賦值給變量p
strcpy(p,“hello”); //段錯誤,因爲p只能存放地址,不能存放字符串。
============================
char A[10];
strcpy(A,“hello”); //可以
============================
char *p;
strcpy(p,“hello”); //段錯誤,因爲p只能存放地址,不能存放字符串。
============================
char A[10]; //在棧區中申請10個字節,使用變量A間接訪問這個數組
char *p = A; //在棧區申請4個字節,使用變量p間接訪問這片內存,將數組的A的首元素的地址賦值給p
strcpy(p,“hello”); //將常量區的hello拷貝到p指向的棧區空間
============================
2、堆區
char *p = malloc(sizeof(char)*10); //在堆區中申請10個字節,使用p間接訪問這片內存空間。
p = “hello”; //給p賦值了常量區hello的地址,就是說現在p不是指向堆,而是指向常量區。
============================
char *p = malloc(sizeof(char)*10); //在堆區中申請10個字節,使用p間接訪問這片內存空間。
strcpy(p,“hello”); //可以,把常量區的hello拷貝到堆區!
四、結構體
1、什麼是結構體?
將多個不同類型的變量加入到一個集合中,這個集合就稱之爲結構體
。
由於結構體中存在不同類型的變量,所以每一個變量都需要用戶自己定義。
可以在結構體中定義:
基本數據類型: char short int long float double
非基本數據類型:int A[3]
、 int*p
、char B[2][3]
、char *px
、char pa[5]
、 int(*pfun)(int)
不可以在結構體中定義:函數
2、 如何定義結構體?
關鍵詞:struct
模型:
struct 結構體名字{
/* 結構體的組成變量 */
例子:
int a;
char b;
int A[3];
int *p;
}; -> 後面記住有一個分號,不然報錯。
其實結構體就是一種新的數據類型。
例子:
struct data{
int a;
char b;
};
在這裏只是先說: “struct data” 這種新類型由 {int a;char b;}組成。
3、如何定義結構體的變量?
struct mydata{
char name[20];
int age;
};
struct mydata -> 新的數據類型
定義變量公式: 數據類型 + 變量名;
定義整型變量 int a;
定義結構體變量 struct mydata A;
4、結構體的指針如何定義?
定義結構體變量: struct mydata A;
1)先寫一個*
2)在*後面寫一個變量的名字 *p
3)確認指針指向的內容是什麼。struct mydata A;
4)將指向的內容的變量名去掉struct mydata
5)將第4步的結果寫在第2步的前面 struct mydata *p
;
結果:struct mydata *p;
變量名: p
數據類型: struct mydata *
5、結構體的變量與指針如何訪問成員?
1)設計結構體模型
struct mydata{
char name[20];
int age;
};
2)定義一個結構體的變量
struct mydata gec;
3)結構體變量使用"."
來對成員進行訪問。
公式: 結構體變量名.成員名字
例子:
strcpy(gec.name,"ggy");
gec.age = 10;
4)結構體指針使用"->"訪問成員。
公式: 結構體指針變量名->成員名字
例子:
strcpy(p->name,"ggy");
p->age = 10;
6、結構體初始化值
1)先定義一個變量,後使用./->對成員進行賦值
struct mydata gec;
strcpy(gec.name,“ggy”);
gec.age = 10;
2)使用初始化列表
struct mydata gec = {“helloworld”,20}; -> 等價於將"helloworld"拷貝到name成員的空間中,20就賦值給age;
struct mydata gec = {“helloworld”}; -> 後面沒有賦值的成員 變量爲0 指針爲NULL
3)使用另外一個結構體給某個結構體整體賦值
struct mydata gec;
struct mydata A = {“hello”,20};
gec = A;
練習: 做一個通訊錄,裏面存放三個同學信息,每一個同學包含: 姓名,年齡,電話號碼 -> 結構體數組。
-
先分別對三個同學註冊。
-
輸出三個同學的全部信息。
#include <stdio.h>
struct mydata{
char name[20];
int age;
char tel[20];
};
int main()
{
struct mydata A[3];
int i; //0~2
for(i=0;i<3;i++)
{
printf("pls input %d name",i+1);
scanf("%s",A[i].name);
printf("pls input %d age",i+1);
scanf("%d",&A[i].age);
printf("pls input %d tel",i+1);
scanf("%s",A[i].tel);
}
printf("====================================================\n");
for(i=0;i<3;i++)
{
printf("%s %d %s\n",A[i].name,A[i].age,A[i].tel);
}
return 0;
}
7、計算結構體類型在內存中佔用的字節數。
基本數據類型:char 、short、int 、long、 float、 double -> 佔用字節數固定。
例子: int a -> 佔用4個字節
自定義結構體數據類型佔用多少個?
struct mydata{
char name[20];
int age;
char tel[20];
};
例子1:
struct mydata{
char name[20];
int age;
};
sizeof(struct mydata) = ? //24
例子2:
struct mydata{
int age;
char name[20];
};
sizeof(struct mydata) = ? //24
例子3:
struct mydata{
int age;
char name;
};
sizeof(struct mydata) = ? //8
例子4:
struct mydata{
int age;
char name;
short a;
};
sizeof(struct mydata) = ? //8
例子5:
struct mydata{
char a;
char b;
short c;
};
sizeof(struct mydata) = ? //4
=例子6:=
struct mydata{
char a;
short b;
char c;
};
sizeof(struct mydata) = ? //6
例子7:
struct mydata{
char a;
short b;
char c;
int d;
int e;
};
sizeof(struct mydata) = ? //16
例子
struct mydata{
char a;
int (*px)[3];
char A[10];
int b;
char **p;
short c;
char a;
short f;
int e;
char B[18];
}; //60
例子
struct mydata{
short a;
int (*px)(int,int);
char b;
char c;
int d;
char A[13];
short e;
int **p;
int f;
char *B[3];
}; //52
計算結構體佔用空間大小的方法:
1)從上往下計算,而不是根據類型的大小來計算。
2)看看當前結構體中最大的成員佔用是2/4 -> 2的話結果就是2的倍數 4的話結果就是4的倍數。
3)如果某個結構體成員計算完,但是當前沒有對齊,那麼剩餘的字節大小就應該與下一個成員大小進行比較(字節對齊)
- 下一個成員的大小大於當前行剩餘的字節大小
結果: 剩餘的字節補0,下一個成員開闢新的一行空間。 - 下一個成員的大小小於/等於當前行剩餘的字節大小
結果:將下一個成員塞進剩餘的字節中
4)全部結構體成員處理完後,看看2的倍數/4的倍數來進行補0。
六、聯合體
1、爲什麼會有聯合體?
爲了解決結構體在內存中佔用比較大情況。
例子:
struct mydata{
char name[20];
int age;
char tel[20];
};
sizeof(struct mydata) = 44; -> 佔用非常多空間 -> 如果定義爲聯合體,則大大節省。
2、聯合體定義方式與結構體一致,只需要修改關鍵詞即可。
struct -> union
例子:
union mydata{
char name[20];
int age;
char tel[20];
};
sizeof(union mydata) = 20
但是使用的時候,只能同時使用一個成員。
計算聯合體的空間大小:
1)看看聯合體哪個成員是佔用空間最大的。
2)看看當前結構體中最大的成員佔用是2/4 -> 2的話結果就是2的倍數 4的話結果就是4的倍數。
3)如果該成員佔用字節數是2/4的倍數,那麼聯合體的大小就是該變量佔用的字節數。
4)如果該成員佔用字節數不是2/4的倍數,那麼看情況來補0。
3、由於聯合體只能同時使用一個成員,不能整體賦值。
例子:
union data{
char name[10];
int age;
};
union data A = {“ggy”,10};
編譯警告; warning: excess elements in union initializer
==========================================================
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
int main()
{
union data A;
strcpy(A.name,"ggy");
printf("A.name = %s\n",A.name);
bzero(A.name,sizeof(A.name));
A.age = 10;
printf("A.age = %d\n",A.age);
return 0;
}
=====================================================
4、在聯合體中,所有成員的起始地址都是一致的。
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
int main()
{
union data A;
printf("A.name:%p\n",A.name);
printf("A.age:%p\n",&A.age);
return 0;
}
執行:
A.name:0xbfb3b220
A.age:0xbfb3b220
5、聯合體也是可以作爲函數的參數
#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
char name[10];
int age;
};
void fun(union data B) //B = A
{
printf("B.name = %s\n",B.name);
return;
}
int main()
{
union data A;
strcpy(A.name,"ggy");
fun(A);
return;
}
七、枚舉類型
1、什麼是枚舉類型?
枚舉類型其實就是int類型常量,意義就是給常量一個身份。
例子: ok -> 1 warning -> 0 error -> -1
2、枚舉類型一般作用地方?
1)可以作用於switch語句。
2)在函數返回值。 return 枚舉類型數據類型
void fun()
{
if(xxxx)
return warning; -> 別人看起來就知道返回值是什麼情況。
if(yyyy)
return ok;
if(zzzz)
return error;
}
3、如何定義枚舉類型?
關鍵詞: enum
模型:
enum mydata{
ok, //如果沒有賦值,第一個成員默認從0開始! //0
warning, //後面沒有賦值的成員默認在前面的基礎+1 //1
error, //2
};
enum mydata{
ok = 1,
warning = 0,
error = -1,
};
enum mydata{
ok = 10,
warning = 20,
error, //21
};
八、typedef關鍵詞
1、什麼是typedef?作用是什麼?
typedef
其實就是 type
+ define
,給一種數據類型
(基本數據類型/非基本數據類型)取一個新的別名
。
例子: 給int這種類型取一個新的名字叫aaa。 -> 很少給基本數據類型取別名 1%
給struct mydata這種數據類型取新的名字叫mydata。 -> 給非基本數據類型取別名 99%
作用: 簡化複雜的數據類型。
2、如何使用typedef給數據類型取別名?
使用格式: typedef + 數據類型 + 新的名字
1)給int這種類型取一個新的名字叫aaa。
typedef int aaa; -> 原則來typedef後面的這個"int aaa"看看,類型是int,剩下的就是aaa就是它的新名字。
在後面的代碼中,定義一個整型變量:aaa a;
例子:
#include <stdio.h>
int main()
{
typedef int aaa; //int 等價於 aaa
aaa a = 10;
printf("a = %d\n",a);
return 0;
}
2)給struct mydata這種數據類型取新的名字叫mydata。
typedef struct mydata{
char name[20];
int age;
}mydata; //struct mydata 等價於 mydata
mydata bbb; -> 定義一個結構體變量。