C語言的函數概念與規則你瞭解嗎

一、函數概念

函數三要素
int fun(int,int,char){xxx}

函數名 (地址)輸入參數輸出參數

1.傳入參數

實參: 調用時傳入的具體值
形參: 函數內部接受的變量

2.返回值

返回函數處理的結果數據(另一種方式是函數外部使用址傳遞參數,來獲得函數處理的數據)

二、函數參數

C語言函數傳參,實際上就是copy的過程,參數類型的區別在於copy的多少的問題,當然類型不同可能存在丟失部分數據情況(copy不完整)

void printAddress(int a)
{
	//printAddress a地址:0xc
    printf("printAddress a地址:%p\n",a);
}
int main()
{
    int a = 12;
    //main a地址:0xc
    printf("main a地址:%p\n",a);
    printAddress(a);
    return 0;
}
void printAddress(int a)
{
	//printAddress a地址:0xffffff9b
    printf("printAddress a地址:%p\n",a);
}
int main()
{
    char a = "ABCDE";
    //main a地址:0xffffff9b
    printf("main a地址:%p\n",a);
    printAddress(a);
    return 0;
}
void printAddress(int a)
{
    //printAddress a地址:0xeccc1e18
    printf("printAddress a地址:%p\n", a);
}
int main()
{
    int a = 12;
    int *p_a;
    p_a = &a;
    //main a地址:0x7ffeeccc1e18
    printf("main a地址:%p\n", p_a);
    printAddress(p_a);
    return 0;
}

三、指針函數-函數名指針化

通過函數三要素可以得出,指針也可以表示函數
int (*p)(int,int,char){xxx} 與指針指向二維數組類似,指針必須要加(),來提高程序對他解讀的優先級,而不是默認從右往左讀

1.指針指向其他函數的函數名(標簽)效果

int main()
{
    int (*printf2)(const char *,...);
    //HelloWOrld
    printf("HelloWOrld\n");
    printf2 = printf;
    //FFFF
    printf2("FFFF\n");
    return 0;
}

2.指針指向其他函數的地址效果

int main()
{
    int (*printf2)(const char *, ...);
    //HelloWOrld 0x7fff204b50b8
    printf("HelloWOrld %p \n", printf);
    printf2 = (int (*)(const char *, ...))0x7fff204b50b8;
    //FFFF
    printf2("FFFF\n");
    return 0;
}

3.通過二級指針,將一組函數線性化

int (*p[5])(int,int);
p[0]=func1;
p[1]=func2;
p[2]=func3;
//使用效果,可以替代switch
p[day](8,9);

四、函數值傳遞和址傳遞

值傳遞:void fun1(int a){…}傳入數據a copy到函數中,函數中數據的修改不影響函數外被傳入的數據
址傳遞:void func2(*a){…}傳入數據地址&a copy到函數中,函數中數據的修改是在該地址上修改的,函數外被傳入的數據也處在該內存地址上,所以會數據被修改

僅查看時用值傳遞
讀寫資源或節省資源時 使用址傳遞

五、函數連續空間的傳遞

連續空間傳遞,一般都會選擇使用址傳遞,使用值傳遞浪費資源過多

1.結構體(變量)

值傳遞示例

struct product_t
{
    char name[20];
    int price;
};

void println(struct product_t product)
{
    strncpy(product.name, "牙刷", 20);
    product.price = 3;
    //println:牙刷
    printf("println:%s \n", product.name);
    //println:3
    printf("println:%d \n", product.price);
}
int main()
{
    struct product_t product;
    strncpy(product.name, "手機", 20);
    product.price = 3666;
    println(product);

    //main:手機
    printf("main:%s \n", product.name);
    //main:3666
    printf("main:%d \n", product.price);
    return 0;
}

址傳遞示例 (通過指針)

struct product_t
{
    char name[20];
    int price;
};

void println(struct product_t *product)
{
    //product[0]等價於*product此處可以替換,但是需要加()讓程序優先識別,即:(*product)
    strncpy(product[0].name, "牙刷", 20);
    product[0].price = 3;
    //println:牙刷
    printf("println:%s \n", product[0].name);
    //println:3
    printf("println:%d \n", product[0].price);
}
int main()
{
    struct product_t product;
    strncpy(product.name, "手機", 20);
    product.price = 3666;
    println(&product);

    //main:牙刷
    printf("main:%s \n", product.name);
    //main:3
    printf("main:%d \n", product.price);
    return 0;
}

2.數組(標簽)

數組int names[10];在定義時,編譯器已經知道其首地址被names標簽描述,因此數組作為形參時隻需要傳遞數組的標簽名和偏移量即可names[10]即可

數組址傳遞數據示例

//C語言現在優化允許使用int p[10],便於閱讀 實際上效果與int *p一致,都是址傳遞
void println(int *p)
{
    p[0] = 77;
    //println:77
    printf("println:%d \n", p[0]);
    //println:11
    printf("println:%d \n", p[1]);
}
int main()
{
    int prices[10] = {22, 11, 55};
    println(prices);

    //main:77
    printf("main:%d \n", prices[0]);
    //main:11
    printf("main:%d \n", prices[1]);
    return 0;
}

數組址傳遞地址示例

//C語言現在優化允許使用int p[10],便於閱讀 實際上效果與int *p一致,都是址傳遞
void println(int *p)
{
    p[0] = 77;
    //println:0x7ffee8964df0
    printf("println:%p \n", &p[0]);
    //println:0x7ffee8964df4
    printf("println:%p \n", &p[1]);
}
int main()
{
    int prices[10] = {22, 11, 55};
    println(prices);

    //main:0x7ffee8964df0
    printf("main:%p \n", &prices[0]);
    //main:0x7ffee8964df4
    printf("main:%p \n", &prices[1]);
    return 0;
}

如上兩段代碼,數組傳遞為址傳遞,數組標簽名本身就是地址的標簽,與指針非常相似

3.連續空間的隻讀

定義參數時使用const修飾指針所指的內存地址即可,如const char *p,這也是開發時默認的規范
讀寫函數形參:char *p;
隻讀函數形參:const char *p
如strncpy函數的源碼:char *stpncpy(char *dst, const char *src, size_t n)

六、函數返回值

返回值與參數一樣,也是copy的原理傳遞,可以copy值,也可以copy地址

char func1()
{
    char s = 'A';
    return s;
}
int main()
{
    char s= func1();
    //41
    printf("%X",s);
    return 0;
}

註意:指針是C語言中返回空間的唯一類型 (無法返回函數內部創建的變量地址,會被回收:異常)
int * func();

char * func1()
{
    return "HelloWorld";
}
int main()
{
    char *p= func1();
    //HelloWorld
    printf("%s",p);
    return 0;
}

工程中一般不使用上述方案(常量區存儲),意義不大,通常使用靜態區存儲局部變量(程序結束才回收),即如下方法

char * func1()
{
    static char abc[] = "HelloWorld";
    return abc;
}
int main()
{
    char *p= func1();
    //HelloWorld
    printf("%s",p);
    return 0;
}

也可以使用堆區存儲malloc()函數中創建的變量,註意使用完畢後一定要釋放內存(free())

char * func1()
{
    char *s = (char *)malloc(50);
    strncpy(s,"HelloWorld",50);
    return s;
}
int main()
{
    char *p= func1();
    //HelloWorld
    printf("%s",p);
    free(p);
    return 0;
}

總結

本章主要為C語言函數概念和規則

本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: