- 定義一個函數(shù), 要求能夠在函數(shù)中修改傳入變量的值
int num = 6;
printf("調(diào)用之前:num = %i\n", num);
// change(num);
change(&num);
printf("調(diào)用之后:num = %i\n", num);
return 0;
}
//void change(int value){
// 結(jié)論: 如果函數(shù)的形參是數(shù)組或者是指針,
// 那么就可以在函數(shù)中修改外界傳入變量的值
void change(int *value){ // 相當(dāng)于 int *value = #
*value = 888;
- 需求: 要定定義一個函數(shù), 在函數(shù)中交換傳入變量的值
void main(){
int num1=10,num2=20;
chang(&num1,&num2);
printf("num1=%i,num2=%i",num1,num2);
}
基本數(shù)據(jù)類型作為形參, 在函數(shù)內(nèi)修改形參, 不會影響到函數(shù)外的實參,所以定義為指針變量,傳進(jìn)來地址
int chang(int *n1,int *n2){
int mid=*n1;
*n1=*n2;
*n2=mid;
}
- 需求: 要求定義一個函數(shù), 可以同時返回兩個變量的和,差,積,商
需要大家知道的是: 在C語言中, 默認(rèn)情況下一個函數(shù)只能返回一個值
如果想讓某一個函數(shù)同時返回多個值, 可以借助指針來實現(xiàn)
#include <stdio.h>
//int sum(int num1, int num2);
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4);
int main()
{
int a = 10;
int b = 20;
// int res = sum(a, b);
// printf("res = %i\n", res);
int d, e, f, g;
test(a, b, &d, &e, &f, &g);
printf("和 = %i\n", d);
printf("差 = %i\n", e);
printf("積 = %i\n", f);
printf("商 = %i\n", g);
return 0;
// printf("return 后面的語句\n");
}
/**先輸出/**,然后按住Ctrl旁邊的Fn+回車鍵,自動生成以下注釋
* @brief test 可以同時返回兩個變量的和,差,積,商
* @param num1 參與運(yùn)算的第一個變量
* @param num2 參與運(yùn)算的第二個變量
* @param res1 和
* @param res2 差
* @param res3 積
* @param res4 商
*/
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4){
*res1 = num1 + num2;
*res2 = num1 - num2;
*res3 = num1 * num2;
*res4 = num1 / num2;
}
注意點:return后面的語句執(zhí)行不到
int sum(int num1, int num2){
// 注意點:
// return的作用是用于結(jié)束當(dāng)前函數(shù)
// 只要函數(shù)執(zhí)行到return, 就會立刻停止往下執(zhí)行
// 所以return 后面不可以編寫任何語句, 因為執(zhí)行不到
return num1 + num2;
// return num1 - num2;
// return num1 * num2;
// return num1 / num2;
}
*/
- 多級指針
前面我們學(xué)習(xí)的指針我們稱之為一級指針
* 什么是多級指針
* 指針中保存的又是其它指針的地址, 我們就稱之為多級指針
* 如何定義多級指針
* 在要保存的指針變量的基礎(chǔ)上加一顆星即可
* 例如: int *p; 如果想保持指針變量p的地址, 只需在定義時多加一顆星即可 int **pp;
*
* 訪問的規(guī)則:pp
* 如果定義就如何訪問, 例如: int *p; 訪問 *p; 例如: int **pp; 訪問 **pp;
*
* 定義指針的規(guī)律:
* 1.將要指向的變量的定義拷貝過來
* 2.再拷貝過來的代碼的類型和變量名稱中間加上一顆星
* 3.修改變量名稱
int num=6//int num;num=6;
int *p=#//int *p;p=#定義指針規(guī)律的意思就是把int num;拷貝過來,在int和num之間加*,在中間靠近誰都沒關(guān)系,然后改變num的名稱為p
int **p2=&p;
printf("&num = %p\n", &num); // &num = 0060FEAC
printf("p = %p\n", p); // p = 0060FEAC
printf("*pp = %p\n", *pp); // *pp = 0060FEAC
printf("&pp = %p\n", &pp); // &pp = 0060FEA4
規(guī)律: 如果想通過多級指針獲取某個變量的值, 那么是幾級指針, 前面就寫幾顆星即可
注意點: 在企業(yè)開發(fā)中, 最多二級指針, 三級頂天了, 四級沒講過
- 指針和數(shù)組
int ages[3] = {1, 3, 5};
for(int i = 0; i < 3; i++){
printf("ages[%i] = %i\n", i, ages[i]);
}
數(shù)組名稱保存的就是數(shù)組占用內(nèi)存最小的那個地址
既然數(shù)組名稱保存的就是地址, 而指針也是用于保存地址的, 所以指針也可以指向數(shù)組
int *p = &ages;
printf("ages = %p\n", ages); // ages = 0060FEA0
printf("&ages = %p\n", &ages); // ages = 0060FEA0
int *p = ages;// int *p = &ages
printf("p = %p\n", p); // 0060FEA0
結(jié)論: 如果利用指針保存數(shù)組的地址之后, 那么 p = ages = &ages;
- 要求你寫出三種訪問數(shù)組元素的寫法
int ages[3] = {1, 3, 5};//第一種
printf("ages[0] = %i\n", ages[0]);
int *p = ages;//第二種
printf("p[0] = %i\n", p[0]);
printf("0[p] = %i\n", 0[p]);//第三種
- 指針遍歷數(shù)組
int ages[3] = {1, 3, 5};
int *p = ages;
// printf("*p = %i\n", *p); // 1
// printf("*(p + 1) = %i\n", *(p + 1)); // 3
// printf("*(p + 2) = %i\n", *(p + 2)); // 5
// printf("*p = %i\n", *p++); // 1
// printf("*p = %i\n", *p++); // 3
// printf("*p = %i\n", *p); // 5
// printf("*p = %i\n", *(--p)); // 3
for(int i = 0; i < 3; i++){
// printf("ages[%i] = %i\n", i, ages[i]);
printf("ages[%i] = %i\n", i, *p++);
}
- 指針和字符串(用指針變量保存字符串)
指針和字符串
* 字符串的本質(zhì)就是數(shù)組, 所以指針也可以指向字符串
* 正式因為如此, 所以定義字符串又多了一種方式
char str1[] = {'l', 'n', 'j', '\0'};
char str2[] = "lnj";//可以寫成str2[i]
char *str4 = "lnj";//同樣可以寫成str2[i]
利用數(shù)組和指針定義字符串的區(qū)別:
* 1. 存儲的位置不同
* 如果是通過數(shù)組定義的字符串, 那么存儲在內(nèi)存的棧中
* 如果是通過指針定義的字符串, 那么存儲在內(nèi)存的常量區(qū)中
*
* 2.由于在內(nèi)存中存儲的位置不一樣, 所以特性也不一樣
* 如果是通過數(shù)組定義的字符串, 我們是可以手動修改
* 如果是通過指針定義的字符串, 我們不用手動修改
char str[] = "lnj";
char *str = "lnj";
printf("str = %s\n", str);
str2[1] = 'T';
printf("str = %s\n", str);
*
* 3.由于在內(nèi)存中存儲的位置不一樣, 所以特性也不一樣
* 如果是通過數(shù)組定義的字符串, 每次定義都會重新開辟存儲空間
* 如果是通過指針定義的字符串, 重復(fù)定義不會重新開辟存儲空間
char str1[] = "lnj";
char str2[] = "lnj";
printf("str1 = %p\n", str1); // 地址不一樣
printf("str2 = %p\n", str2);
char *str1 = "lnj";
char *str2 = "lnj";
printf("str1 = %p\n", str1); // 地址一樣
printf("str2 = %p\n", str2);
- 接收和打印,字符串?dāng)?shù)組和指針的區(qū)別
1.接收字符串的時候, 只能通過字符數(shù)組, 不能通過字符指針
char str[10];
char *str;//程序編譯是不報錯,運(yùn)行時出現(xiàn)問題,這樣定義接收不到
scanf("%s", str);
rintf("str = %s\n", str);
2.如果函數(shù)中返回的字符串是通過數(shù)組創(chuàng)建的, 那么外界無法獲取
如果函數(shù)中返回的字符串是通過指針創(chuàng)建的, 那么外界可以獲取
char *res = demo();
printf("res = %s\n", res);
char* demo(){//返回值為指針類型,所以有星號
char str[] = "lnj";//函數(shù)外面接收不到
char *str = "lnj"
return str;//str保存的是常量區(qū)lnj\0的地址
/ 注意點: 學(xué)習(xí)了指針之后, 建議將過去形參的數(shù)組類型修改為指針類型
int ages[3] = {1, 3, 5};
test(ages);
printf("ages[1] = %i\n", ages[1]);
//void test(int nums[]){
void test(int *nums){//由于test(ages)傳進(jìn)來的就是地址,用指針更形象
nums[1] = 6;
- 字符串?dāng)?shù)組
字符串?dāng)?shù)組
// 字符串就是一個數(shù)組, 所以字符串?dāng)?shù)組就是一個二維數(shù)組
char str[][] = {//字符串?dāng)?shù)組表示方法
"lnj",
"abc",
"def"
};
// 字符串?dāng)?shù)組的第二種格式
char *str[] = {
"lnj",
"abc",
"def"
};
- 練習(xí)
1.實現(xiàn)strlen()函數(shù)
char *str = "lnj666";//此處注意字符串為char類型,形參處也為char類型
// char str[10] = {'l', 'n', 'j', '\0'};
// 注意點: strlen計算的是保存了多少個字符串
// 計算規(guī)則, 就是返回字符串中\(zhòng)0之前有多少個字符
int res = myStrlen(str);
printf("res = %i\n", res);
return 0;
}
int myStrlen(char *str){
// 1.定義變量記錄當(dāng)前有多少個
int count = 0;
while(*str++){
count++;
}
// while(*str++ != '\0'){ 方法二
// count++;
// }
// while(str[count] != '\0'){ 方法三
// count++;
// }
return count;
}
2.實現(xiàn)strcat()函數(shù)
注意點: 第一個參數(shù)必須足夠大
//char *str1 = "lnj"; // 不能用于拼接,因為不能看出第一個字符空間大小
// char *str2 = "it666";
char str1[10] = "lnj";
char str2[10] = "it666";
myStrcat(str1, str2);
printf("str1 = %s\n", str1);
return 0;
}
void myStrcat(char *str1, char *str2){
方法一:
while(*str1){
str1++;
}
while(*str2){
*str1 = *str2;
str1++;
str2++;
}
*str1='\0';
}
方法二:
// 1.拿到第一個數(shù)組\0的位置
int index = 0;
while(str1[index] != '\0'){
index++;
}
// 2.將第二個數(shù)組添加到第一個數(shù)組末尾
int count = 0;
while(str2[count] != '\0'){
str1[index] = str2[count];
count++;
index++;
}
3.實現(xiàn)strcpy()
同樣不能用指針定義字符串?dāng)?shù)組,
int main()
{
char str1[9] = "lnj";
char str2[10] = "it";
// strcpy(str1, str2);
myStrcpy(str1, str2);
printf("str1 = %s\n", str1);
return 0;
}
void myStrcpy(char *str1, char *str2){
while(*str2){
*str1 = *str2;
str1++;
str2++;
}
*str1 = '\0';
}
/*
void myStrcpy(char *str1, char *str2){
int index = 0;
while(str2[index] != '\0'){
str1[index] = str2[index];
index++;
}
str1[index] = '\0';
}
*/
4.實現(xiàn)strcmp()
int main()
{
// strcmp
char str1[9] = "125";
char str2[10] = "124";
// 兩個字符串相同返回0
// 第一個參數(shù)小于第二個參數(shù) -1 負(fù)數(shù)
// 第一個參數(shù)大于第二個參數(shù) 1 正數(shù)
// 如果前面的內(nèi)容都相同, 第一個參數(shù)的個數(shù)小于第二個參數(shù), -1 負(fù)數(shù)
// 如果前面的內(nèi)容都相同, 第一個參數(shù)的個數(shù)大于第二個參數(shù), 1 正數(shù)
// int res = strcmp(str1, str2);
int res = myStrcmp(str1, str2);
printf("res = %i\n", res);
return 0;
}
int myStrcmp(char *str1, char *str2){
// while(*str1 != '\0' || *str2 != '\0'){
while(*str1 || *str2){
if(*str1 > *str2){
return 1;
}else if(*str1 < *str2){
return -1;
}
str1++;
str2++;
}
return 0;
}
/*方法二:
int myStrcmp(char *str1, char *str2){
int index = 0; // 3
// \0 \0
while(str1[index] != '\0' || str2[index] !='\0'){
// 3 3
if(str1[index] > str2[index]){
return 1;
}else if(str1[index] < str2[index]){
return -1;
}
index++;
}
return 0;
}
*/
-
指向函數(shù)的指針
- 指針變量的作用: 專門用于保存地址
* 指向函數(shù)的指針
* 計算機(jī)也會給函數(shù)分配存儲空間, 既然函數(shù)會分配內(nèi)存空間,
* 所以函數(shù)也有自己的地址, 所以指針變量也可以保存函數(shù)的地址
*
* 經(jīng)過前面的學(xué)習(xí), 我們知道數(shù)組名保存的就是數(shù)組的地址
* 函數(shù)和數(shù)組很像, 函數(shù)名中保存的就是函數(shù)的地址
*
* 如何定義指針變量?
* 1.將需要保存變量的定義拷貝過來
* 2.在數(shù)據(jù)類型和變量名稱中間添加一個*
* 3.修改變量名稱
*
* 如何定義保存函數(shù)的指針變量
* 1.將函數(shù)的什么拷貝過來
* 2.在函數(shù)返回值和函數(shù)名稱總監(jiān)添加一個*
* 3.修改函數(shù)名稱
* 4.注意點: 需要將*和變量名稱用括號括起來
*
* 我們說過指向函數(shù)的指針和指向數(shù)組的指針很像
* 如果是數(shù)組, 我們可以直接將數(shù)組名稱賦值給一個指針變量
* 如果是函數(shù), 我們也可以直接將函數(shù)名稱賦值給一個指針變量
*
* 如果一個指針指向了數(shù)組, 那么訪問數(shù)組就有了三種方式
* 數(shù)組名稱[索引];
* 指針變量名稱[索引]
* *(指針編碼名稱 + 索引)
*
* 如果一個指針指向了函數(shù), 那么訪問方式也有多種方式
* 函數(shù)名稱();
* ( *指針變量名稱)();
* 指針變量名稱();
- 指針變量的作用: 專門用于保存地址
int num;
int *p;
p = #
int ages[3] = {1, 3, 5};
int *p;
p = ages;
printf("ages[1] = %i\n", ages[2]);//三種方式訪問數(shù)組
printf("p[1] = %i\n", p[2]);
printf("p[1] = %i\n", *(p+2));
test(); // 第一種方式
void (*funcP)();
funcP = &test;//或者 funcP = test;
(*funcP)(); // 第二種方式
void (*funcP)();
funcP = &test;//或者 funcP = test;
funcP(); // 第三種方式
void test(){
printf("test\n");
}
- 練習(xí):定義指針指向這幾個函數(shù)
void (*p1)();
p1 = say;
p1();
void (*p2)(int, int);
p2 = sum;
p2(10, 20);
int (*p3)(int a, int b);
p3 = minus;
int res = p3(10, 20);
printf("res = %i\n", res);
char* (*p4)();
p4 = test;
char* res2 = p4();
printf("res2 = %s\n", res2);
return 0;
}
void say(){
printf("Hello World\n");
}
void sum(int a, int b){
printf("res = %i\n", a + b);
}
int minus(int a, int b){
return a - b;
}
char* test(){
char* name = "lnj";
return name;
}
- 要求一個函數(shù)既可以計算兩個變量的和, 也可以計算兩個變量的差
// int res1 = sum(10, 20);
// printf("res1 = %i\n", res1);
// int res2 = minus(10, 20);
// printf("res2 = %i\n", res2);
int res3 = test(10, 20, sum);//把sum地址傳給test
printf("res3 = %i\n", res3);
int res4 = test(10, 20, minus);
printf("res4 = %i\n", res4);
return 0;
}
注意點: 指向函數(shù)的指針,作為函數(shù)的形參時, 指針變量的名稱, 就是形參的名稱
// 如果指向函數(shù)的指針作為函數(shù)的參數(shù), 那么這個可稱之為回調(diào)函數(shù)
// 這里相當(dāng)于, 給test函數(shù)傳入了一個sum函數(shù)或者minus函數(shù)
// 然后又在test函數(shù)中調(diào)用了sum函數(shù)或者minus函
int test(int num1, int num2, int (*funP)(int, int)){
return funP(num1, num2);//把num1和num2的值傳給sum函數(shù)
int sum(int num1, int num2){
return num1 + num2;
}
int minus(int num1, int num2){
return num1 - num2;
}
- 作業(yè)
1.要求從鍵盤輸入一個字符串, 并且字符串中可以出現(xiàn)空格
- 2.將用戶輸入的字符串, 單詞的首字母變成大寫, 單詞用空格劃分
- hello world; --> Hello World;
- 3.將用戶輸入的字符串, 單詞的首字母編程小寫, 單詞用空格劃分
- Hello World; --> hello world;
- 4.要求定義一個函數(shù), 既可以處理將首字母變?yōu)榇髮? 也可以處理將首字母變?yōu)樾?/li>
- 需要用到指向函數(shù)的指針
- 結(jié)構(gòu)體
/*
* 什么是結(jié)構(gòu)體?
* 結(jié)構(gòu)體時構(gòu)造類型的一種
*
* 構(gòu)造類型前面我們已經(jīng)學(xué)習(xí)過了數(shù)組:
* 數(shù)組的作用是用于存儲一組相`同類型`的數(shù)據(jù)
* 結(jié)構(gòu)體的作用是用于存儲一組`不同類型`的數(shù)據(jù)
*
* 保存一個人的信息
* 姓名/年齡/身高 ...
* char *
* int
* double
*
* 如何定義結(jié)構(gòu)體變量
* 1.先定義結(jié)構(gòu)體類型
* 2.通過結(jié)構(gòu)體的類型定義結(jié)構(gòu)體變量
*
* 如何定義結(jié)構(gòu)體類型?
* struct 結(jié)構(gòu)體類型名稱{
* 數(shù)據(jù)類型 屬性名稱;
* 數(shù)據(jù)類型 屬性名稱;
* ... ...
* };
*
* 如何定義結(jié)構(gòu)體變量
* struct 結(jié)構(gòu)體類型名稱 變量名稱;
*
* 如何訪問結(jié)構(gòu)體的屬性
* 結(jié)構(gòu)體變量名稱.結(jié)構(gòu)體屬性名稱;
*/
// 1.定義結(jié)構(gòu)體類型
struct Person{
char *name; // name我們稱之為結(jié)構(gòu)體的屬性名稱
int age; // age也稱之為結(jié)構(gòu)體的屬性名
double height; // height也稱之為結(jié)構(gòu)體的屬性名稱
};
// 2.定義結(jié)構(gòu)體變量
struct Person p;
// 3.使用結(jié)構(gòu)體變量
// int ages[3] = {1, 3, 5};
// ages[0] = 1;
// 格式: 結(jié)構(gòu)體變量名稱.結(jié)構(gòu)體屬性名稱
p.name = "lnj";
p.age = 35;
p.height = 1.9;
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
return 0;
}
- 結(jié)構(gòu)體變量初始化的幾種方式
1.定義結(jié)構(gòu)體類型
struct Dog{
char *name;
int age;
double height;
2.1先定義后初始化
// struct Dog dd;
// dd.name = "ww";
// dd.age = 1;
// dd.height = 1.5;
2.2定義的同時初始化
// struct Dog dd = {"ww", 1, 1.5};
注意點: 如果在定義的同時初始化, 那么初始化的順序必須和結(jié)構(gòu)體類型中的順序一致
struct Dog dd = {.age = 1, .name = "ww", .height = 1.5};//也可以指定初始化,可以部分初始化.
3.特殊的初始化方式
數(shù)組只能在定義的同時完全初始化, 不能先定義再完全初始化
但是結(jié)構(gòu)體既可以在定義的同時完全初始化, 也可以先定義再完全初始
// int ages[3] = {1, 3, 5};
// int ages[3];
// ages = {1, 3, 5};//數(shù)組不可以
// 企業(yè)開發(fā)不推薦這樣編寫
struct Dog{
char *name;
int age;
double height;
};
struct Dog dd;
dd = (struct Dog){"ww", 1, 1.5};//強(qiáng)制轉(zhuǎn)換
printf("name = %s\n", dd.name);
printf("name = %i\n", dd.age);
printf("name = %lf\n", dd.height);
- 定義結(jié)構(gòu)體變量的方式
1.先定義結(jié)構(gòu)體類型, 再定義結(jié)構(gòu)體變量
/*
struct Person{
char *name;
int age;
double height;
};
struct Person p1;
struct Person p11;
*/
2.定義結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量
/*
struct Person{
char *name;
int age;
double height;
} p2;
p2.name = "lnj";
printf("name = %s\n", p2.name);
struct Person p22;
*/
3.定義結(jié)構(gòu)體類型的同時省略結(jié)構(gòu)體名稱, 同時定義結(jié)構(gòu)體變量
// 匿名結(jié)構(gòu)體
// 特點: 結(jié)構(gòu)體類型只能使用一次
struct{
char *name;
int age;
double height;
} p3;
p3.name = "it666";
printf("name = %s\n", p3.name);
return 0;
- 結(jié)構(gòu)體作用域(和變量相同)
- 結(jié)構(gòu)體數(shù)組及初始化
struct Person{
char *name;
int age;
double height;
};
方法一:先定義在初始化
// struct Person p1 = {"lnj", 35, 1.90};
// struct Person p2 = {"zs", 22, 1.2};
// struct Person p3 = {"ls", 33, 1.4};
// struct Person p4 = {"ww", 56, 1.8};
數(shù)據(jù)類型 數(shù)組名稱[元素個數(shù)];
struct Person ps[4];
ps[0] = p1;//ps[0] = {"lnj", 35, 1.90};下面也如此
ps[1] = p2;
ps[2] = p3;
ps[3] = p4;
方法二:定義同時初始化
struct Person ps[4] ={
{"lnj", 35, 1.90},
{"zs", 22, 1.2},
{"ls", 33, 1.4},
{"ww", 56, 1.8},
};
- 結(jié)構(gòu)體內(nèi)存分析
注意點:
* 給整個結(jié)構(gòu)體變量分配存儲空間和數(shù)組一樣, 從內(nèi)存地址比較大的開始分配
* 給結(jié)構(gòu)體變量中的屬性分配存儲空間也和數(shù)組一樣, 從所占用內(nèi)存地址比較小的開始分配
*
* 注意點:
* 和數(shù)組不同的是, 數(shù)組名保存的就是數(shù)組首元素的地址
* 而結(jié)構(gòu)體變量名, 保存的不是結(jié)構(gòu)體首屬性的地址
struct Person{
int age;
int score;
};
struct Person p;
printf("p = %p\n", p); // p = 00000077
printf("&p = %p\n", &p); // &p = 0060FEA8
printf("&p.age = %p\n", &p.age); // &p.age = 0060FEA8
printf("&p.score = %p\n", &p.score); // &p.score = 0060FEAC
//結(jié)構(gòu)體在分配內(nèi)存的時候, 會做一個內(nèi)存對齊的操作
// 會先獲取所有屬性中占用內(nèi)存最大的屬性的字節(jié)
// 然后再開辟最大屬性字節(jié)的內(nèi)存給第一個屬性, 如果分配給第一個屬性之后還能繼續(xù)分配給第二個屬性, 那么就繼續(xù)分配
// 如果分配給第一個屬性之后, 剩余的內(nèi)存不夠分配給第二個屬性了, 那么會再次開辟最大屬性直接的內(nèi)存, 再次分配
//以此類推
- 結(jié)構(gòu)體指針
結(jié)構(gòu)體指針
* 因為結(jié)構(gòu)體變量也會分配內(nèi)存空間, 所以結(jié)構(gòu)體變量也有內(nèi)存地址, 所以也可以使用指針保存結(jié)構(gòu)體變量的地址
*
* 規(guī)律:
* 定義指向結(jié)構(gòu)體變量的指針的套路和過去定義指向普通變量的一樣
*
* 如果指針指向了一個結(jié)構(gòu)體變量, 那么訪問結(jié)構(gòu)體變量的屬性就有3種方式
* 結(jié)構(gòu)體變量名稱.屬性名稱;
* (*結(jié)構(gòu)體指針變量名稱).屬性名稱;
* 結(jié)構(gòu)體指針變量名稱->屬性名稱;
struct Person{
char *name;
int age;
double height;
};
struct Person per = {"lnj", 35, 1.9};//變量名為per
struct Person *p;//定義一個指針變量p,struct Person為結(jié)構(gòu)體類型
p = &per;
printf("per.name = %s\n", per.name);
printf("per.name = %s\n", (*p).name);
printf("per.name = %s\n", p->name);
- 結(jié)構(gòu)體的嵌套(提高代碼的復(fù)用性)
struct Person{
char *name;
int age;
// 出生年月
int year;
int month;
int day;
// 死亡日期
int year2;
int month2;
int day2;
// 讀幼兒園日期
// 讀小學(xué)日期
// 讀中學(xué)日期
所以可以把年月日寫成一個結(jié)構(gòu)體
// 1.定義了一個日期的結(jié)構(gòu)體類型
struct Date{
int year;
int month;
int day;
};
// 2.定義一個人的結(jié)構(gòu)體類型
struct Person{
char *name;
int age;
struct Date birthday;
};
struct Person p = {"lnj", 35, {2020, 12, 12}};
printf("name = %s\n", p.name);
printf("name = %i\n", p.age);
printf("name = %i\n", p.birthday.year);
printf("name = %i\n", p.birthday.month);
printf("name = %i\n", p.birthday.day);
- 結(jié)構(gòu)體和函數(shù)(強(qiáng)調(diào)結(jié)構(gòu)體作用域和變量相同)
1.雖然結(jié)構(gòu)體是構(gòu)造類型, 但是結(jié)構(gòu)體變量之間的賦值
* 和基本數(shù)據(jù)類型賦值一樣, 是拷貝
2. 注意點: 定義結(jié)構(gòu)體類型不會分配存儲空間
* 只有定義結(jié)構(gòu)體變量才會分配存儲空間
struct Person{
char *name;
int age;
};
struct Person p1 = {"lnj", 35};
struct Person p2;
p2 = p1;
p2.name = "zs";
printf("p1.name = %s\n", p1.name); // lnj
printf("p2.name = %s\n", p2.name); // zs
把p1的name更改寫在函數(shù)中,更改后內(nèi)存會釋放掉,最終name值不變
struct Person p1 = {"lnj", 35};
printf("p1.name = %s\n", p1.name); // lnj
test(p1);
printf("p1.name = %s\n", p1.name); // lnj
return 0;
}
void test(struct Person per){
per.name = "zs";
}
- 共用體(企業(yè)開發(fā)中用的比較少)
共用體
*
* 共用體的格式:
* union 共用體名稱{
* 數(shù)據(jù)類型 屬性名稱;
* 數(shù)據(jù)類型 屬性名稱;
* ... ...
* }
* 共用體定義的格式和結(jié)構(gòu)體只有關(guān)鍵字不一樣, 結(jié)構(gòu)體用struct,共用體用union
*
* 共用體特點:
* 結(jié)構(gòu)體的每個屬性都會占用一塊單獨的內(nèi)存空間, 而共用體所有的屬性都共用同一塊存儲空間(同樣先分配占用屬性最大的字節(jié)數(shù))
* 只要其中一個屬性發(fā)生了改變, 其它的屬性都會受到影響
*
* 應(yīng)用場景:
* 同一個變量, 在不同的時刻,需要表示不同類型數(shù)據(jù)的時候, 我們就可以使用共用體
union Test{
int age;
char ch;
};
union Test t;
printf("sizeof(p) = %i\n", sizeof(t));
t.age = 33;
printf("t.age = %i\n", t.age); // 33
t.ch = 'a';
printf("t.ch = %c\n", t.ch); // a
printf("t.age = %i\n", t.age); // 97,,此時原來age的空間已經(jīng)給ch用了
- 枚舉(開發(fā)中經(jīng)常用到)
枚舉?
* 枚舉用于提升代碼的閱讀性, 一般用于表示幾個固定的值
* 所以還有一個名稱, 叫做枚舉常量
*
* 如果某些變量的取值是固定的, 那么就可以考慮使用枚舉來實現(xiàn)
*
* 枚舉的格式:
* enum 枚舉類型名稱{
* 取值1,
* 取值2,
* };
* 注意點: 和結(jié)構(gòu)體,共用體不同, 枚舉是用逗號隔開
*
* 規(guī)范:
* 枚舉的取值一般以K開頭,后面跟上枚舉類型名稱, 后面再跟上表達(dá)的含義
* K代表這是一個常量
* 枚舉類型名稱, 主要是為了有多個枚舉的時候, 方便區(qū)分
* 含義, 你想表達(dá)的意思
*
* 枚舉的取值:
* 默認(rèn)情況下從0開是取值, 依次遞增
* 也可以手動指定從幾開始, 依次遞增,例如KGenderMale = 9,從9開始遞增
enum Gender{
KGenderMale , //打印輸出為0,可寫成male,但是 KGenderMale表達(dá)更清晰,編寫的時候會提示輸出
KGenderFemale, // 1,可寫成female
// 2 ... ...
};
struct Person{
char *name; // 姓名
int age; // 年齡
enum Gender gender; // 性別
};
struct Person p1;
p1.name = "lnj";
p1.age = 58;
p1.gender = KGenderFemale;
struct Person p2;
p2.name = "周芷若";
p2.age = 88;
p2.gender = KGenderFemale;
- 枚舉的作用域(和結(jié)構(gòu)體類型的作用域一樣, 和變量的作用域一樣)
- 枚舉的定義方式(和結(jié)構(gòu)體相同)
2.1先定義枚舉類型, 再定義枚舉變量
enum Gender{
KGenderMale,
KGenderFemale,
};
enum Gender g1;
2.2定義枚舉類型的同時, 定義枚舉變量
enum Gender{
KGenderMale,
KGenderFemale,
} g2;
2.3定義枚舉類型的同時,定義枚舉變量 ,并且省略枚舉類型名稱
enum{
KGenderMale,
KGenderFemale,
} g3;
回顧局部變量和全局變量
* 局部變量:
* 概念: 定義在{}中,函數(shù)中,形參都是局部變量
* 作用域: 從定義的那一行開始, 直到遇到}結(jié)束或者遇到return為止
* 存儲的位置: 局部變量會存儲在內(nèi)存的棧區(qū)中, 會隨著定義變量的代碼執(zhí)行分配存儲空間, 會隨著作用域的結(jié)束自動釋放
* 特點:
* 相同作用域類, 不能出現(xiàn)同名的局部變量
* 如果不同作用域內(nèi)有相同名稱的變量, 那么在訪問時, 內(nèi)部的會覆蓋外部的(就近原則)
*
* 全局變量:
* 概念: 定義在{}外或者函數(shù)外的變量, 都是全局變量
* 作用域: 從定義的那一行開始, 直到文件末尾
* 存儲的位置: 全局變量會存儲在內(nèi)存的靜態(tài)區(qū)中, 會隨著程序的啟動分配存儲空間, 隨著程序的結(jié)束釋放存儲空間
* 特點:
* 如果有多個同名的全局變量, 那么也只會分配一次存儲空間, 多個同名的全局變量共用同一塊存儲空間
- 變量修飾符(auto ,register,extern,static)
1.修飾局部變量(auto ,register)
- auto和register都是用于修飾局部變量的
auto int num = 9;
register int num = 9;
* 它們年的作用是修飾編程的存儲特性
* auto: 特點就是告訴編譯器, 局部變量離開作用域自動釋放
* 默認(rèn)情況下所有局部變量都是auto的, 所以這一句廢話, 所以了解--> 忘記 / 即可
*
* register: 特點就是告訴編譯器, 將局部變量存儲到CPU寄存器中
* 好處就是訪問的速度會更快, 但是在不同平臺,不同編譯器下, 會做不同的優(yōu)化,寄存器存儲空間非常有限,不一定能夠存儲進(jìn)去, 所以還是一句廢話, 所以了解 -->
2.static
2.1static對局部變量的作用
如果利用static修飾局部變量, 那么會將局部變量的存儲區(qū)域從棧區(qū)移動到靜態(tài)區(qū)
* 靜態(tài)區(qū)只有程序結(jié)束才會釋放
void calculate(int r){
// PI使用的概率非常大, 如果是一個局部變量的話, 每次調(diào)用都會重新開辟存儲空間, 這樣性能不好
// 如果PI是static的變量, 那么只會開辟一次, 那么性能就會好很多
static double pi = 3.1415926;
return r * r * pi;
}
2.2 static對全局變量的作用
* 定義一個內(nèi)部的全局變量,
* 1.該變量只能在定義的文件中使用, 不能在其它文件中使用,例:在dashen.c文件中
* 2.并且該變量會獨占一塊內(nèi)存空間
*
* 全局變量的特性:
* 可以定義多個同名的全局變量, 多個同名的全局變量共享一塊內(nèi)存空間
* 哪怕不是同一個文件中的同名全局變量, 也會共享同一塊內(nèi)存空間
* 問題:
* 這樣是不是會導(dǎo)致數(shù)據(jù)混亂,因此引入static,在其他文件中無法使用
*
* 注意點:
* 局部變量如果沒有初始化, 里面存儲的是垃圾數(shù)據(jù)
* 全局變量如果沒有初始會, 系統(tǒng)會自動初始化為0
2.3 static修飾函數(shù)
*代表這事一個內(nèi)部函數(shù), 只能在當(dāng)前文件中使用
* 如果一些內(nèi)部函數(shù)不想提供給外界使用, 那么就可以給函數(shù)添加一個static
*static必須寫到函數(shù)的實現(xiàn)中才有效, 不能寫到函數(shù)的聲明中
* 并且如果一個函數(shù)已經(jīng)被聲明為static的了, 那么在.h文件中就不要編寫該函數(shù)的聲明了
3.extern
- extern對局部變量的作用
* extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
* extern一般用于全局變量, 至今沒見過有人用extern來修飾局部變量
*
*
* extern對全局變量的作用
* extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
* exter只能用于全局變量, 不能用于局部變量
*
* 原因:
* 局部變量, 只有執(zhí)行到那一行代碼才會分配存儲空間, 所以哪怕聲明了 ,但是在使用時還是沒有分配, 所以還是不能存儲數(shù)據(jù)
* 全局變量, 會隨著程序的啟動分配存儲空間, 所以只要聲明了, 使用時已經(jīng)分配好了存儲空間, 一定能夠使用, 一定能夠存儲數(shù)據(jù)
*
*
如果利用extern修飾函數(shù), 代表這是一個外部函數(shù), 其它文件中也可以使用
* 注意點: 默認(rèn)情況下所有函數(shù)都是外部函數(shù), 所有的函數(shù)都可以在其它文件中訪問, 所以extern是一個廢物
extern必須寫到函數(shù)的實現(xiàn)中才有效, 不能寫到函數(shù)的聲明中
{extern int num;
num = 998;
printf("num = %i\n", num);
return 0;
} int num;
- 企業(yè)開發(fā)中大部分都是多人開發(fā), 多人開發(fā)就是多個人一起寫一個項目,所以在企業(yè)開發(fā)中, 都是多人同時操作多個不同的文件
例如: 現(xiàn)在有兩個員工
* 一個是菜鳥, 一個是大神
* 調(diào)用大神寫好的代碼
* 編寫主要的業(yè)務(wù)邏輯代碼
*
在企業(yè)開發(fā)中如何進(jìn)行多人開發(fā)
* 一般情況下會將(函數(shù)的實現(xiàn)), 編寫到.c文件中, 同時會將.c文件中需要暴露給外界使用的方式名稱的聲明寫到.h文件中
* 為什么編寫了.c文件還需要編寫一個.h文件, 原因很簡單, (函數(shù)的實現(xiàn))是你編寫的, 那么函數(shù)的作用,形參你最了解, 所以應(yīng)該由你來編寫
注意:
* 在企業(yè)開發(fā)中, 其它人不需要關(guān)系函數(shù)具體是如何實現(xiàn)的, 只需要關(guān)心如何使用這個函數(shù)即可
* 所以(函數(shù)的實現(xiàn))和聲明都應(yīng)該讓同一個人來完成
*
* main函數(shù)中一般為主要業(yè)務(wù)邏輯,不能有太多冗余代碼
* #include的作用:
* 將后面指定文件中的內(nèi)容拷貝到當(dāng)前文件中
* <>從系統(tǒng)的環(huán)境變量中去拷貝, 一般情況下只有用到系統(tǒng)函數(shù)才使用<>
* " "從指定的路徑中去拷貝, 一般情況下使用同事/自己編寫的.h文件都用""
#include "ds.h" // 導(dǎo)入大神編寫的.h文件,例:#include "D:\WWW\ds.h"
本質(zhì)就是將大神編寫的.h文件中的代碼拷貝過來