簡體   English   中英

在 printf 和格式說明符中使用 & 符號

[英]The usage of an ampersand(&) in printf and format specifiers

#include <stdio.h>
#include <stdlib.h>

int main() {
    char  a;
    printf("What? \t");

    scanf("%s", &a);

    printf("U have to %s", a);
    return 0;
}

每當我構建並運行此代碼並在%s中輸入一個值時,我都會收到一個錯誤,並且調試程序會停止工作並關閉。 但是當我像這樣使用&符號時:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char  a;
    printf("What? \t");

    scanf("%s", &a);

    printf("U have to %s", &a);
    return 0;
}

printf中......它有效。 這是為什么? 它在格式說明符之間也有所不同,例如當在scanf中使用%c%d時,不需要在printf中放置& (和號)符號。 為什么會發生這種情況,它是否與數據類型有關,以及哪個格式說明符得出了這個結果?

(對不起我的英語不好。我不是以英語為母語的人,這是我第一次來這里)。

你在這里看到的是一個經典的代碼示例,它似乎可以工作,但出於錯誤的原因。

讓我們回顧一些關於printfscanf事情。 格式說明符%d用於int類型的值。 你可以像這樣讀取一個整數:

int i;
scanf("%d", &i);

你可以像這樣打印出來:

printf("%d\n", i);

為什么一個使用&而一個不使用? 好吧,C 使用所謂的“按值傳遞”。 如果我們寫

scanf("%d", i);    /* WRONG */

我們會將i的值傳遞scanf 但是我們不想將i的(舊)值傳遞給scanf ,我們希望scanf讀取一個新值,並將其存儲到i 換句話說,我們希望scanf實際上將i的新值傳回給我們。 為了讓它起作用,我們改為將一個指向變量i指針傳遞給scanf ,我們希望它在其中存儲剛剛讀取的整數。 這就是&所做的——它生成一個指向i的指針。

另一方面,當我們調用printf ,傳遞參數的常規方式工作得很好。 我們確實想將i的值傳遞給printf以便它可以打印出來。 如果我們打電話

printf("%d\n", &i);    /* WRONG */

它不起作用,因為printf需要一個int ,而在這里我們錯誤地將它傳遞給了一個指向int的指針。

所以現在我們知道對於帶有%d整數, printf一個intscanf一個指向int的指針。

讓我們談談字符。 %c格式用於字符。 我們可以用scanf讀取一個字符:

char c;
scanf("%c", &c);

我們可以用printf打印它:

printf("%c\n", c);

同樣,模式完全相同。 scanf需要一個指針,以便它可以填充值,因此我們傳遞&c 但是printf只需要這個值,所以我們傳遞普通的c

現在我們來看看字符串。 C 中的字符串是字符數組 此外,C 中的字符串總是以特殊的空字符'\\0'結尾,它標志着字符串的結束。 所以如果我們想聲明一個可以包含最多 9 個字符長的字符串的變量,我們可能會寫

char s[10];

這給了我們 9 個字符的空間,加上終止'\\0'

但是數組在 C 中是特殊的:每當你將一個數組傳遞給一個函數,或者當你做任何需要數組“值”的事情時,你得到的(編譯器自動為你生成的)是一個指向數組的第一個元素。

這意味着要使用scanf%s讀取字符串,我們可以調用:

scanf("%s", s);

“但是&在哪里?”,你問。 “我以為你在調用scanf時總是需要& !”

嗯,不完全是。 調用scanf時總是需要一個指針 事實上,當你調用scanf("%s", s) ,就像你寫的一樣

scanf("%s", &s[0]);

當您將%sscanf ,它需要一個指向幾個字符中第一個字符的指針,即指向字符數組開頭的指針,它應該從這里開始寫入它讀取的字符串。 (它怎么知道數組有多大?如果用戶輸入的字符串太長而無法放入數組怎么辦?我們稍后會討論這些問題。)

當然,您也可以使用%s打印字符串,它看起來像這樣:

printf("%s\n", s);

再一次,這就像你寫的一樣

printf("%s\n", &s[0]);

當您將%sprintf ,它需要一個指向它應該開始打印的幾個字符中的第一個的指針,直到它找到終止的'\\0'字符。

所以%sprintfscanf是特殊的,因為字符串是特殊的(因為數組是特殊的)。 使用%d%c以及幾乎所有其他格式說明符,您通常在調用scanf時需要& ,而在調用printf時通常不需要& 但是對於%s ,您通常不需要&用於printfscanf

(如果我們仔細考慮一下,例外並不是scanf%s不需要& 。記住,規則是, scanf總是需要指針。唯一原因scanf%s不需要'不需要&是當你傳遞一個數組時,你會自動得到一個指向數組第一個元素的指針。所以這個例外實際上是printf%sprintf%s確實需要一個指針,原因是printf%s旨在期望一個指針是沒有辦法不給它一個:它必須接受一個指針,因為對於字符串,這就是你最終總是給它的。)

所以%s的規則是scanf需要一個指向幾個字符中第一個字符的指針,而printf需要一個指向幾個字符中第一個字符的指針。

現在,拋開所有這些背景,我們可以查看您的代碼。 你基本上寫了

char c;
scanf("%s", &c);

起初,這似乎有點,差不多,幾乎是正確的。 scanf%s想要一個指向字符的指針,你給了它&c ,它是一個指向字符的指針。 但是%s真的想要一個指向幾個字符中第一個的指針。 但是你給了它一個指向單個字符的指針。 因此,當用戶鍵入字符串時,鍵入的第一個字符將存儲在c ,但其余字符以及終止的'\\0'將被寫入變量c右側某處的未分配內存中。 他們將覆蓋(“clobber”)可能用於其他用途的內存。 這是一個嚴重的問題,但它可能不會立即變得明顯。

最后,您嘗試使用printf再次打印出來。 你第一次嘗試

printf("%s\n", c);    /* WRONG */

但這根本不起作用。 原因是帶有printf %s需要一個指向char的指針,但您給了它一個普通的char 假設c包含字母'A' 這最終會要求printf轉到地址 65 並開始打印字符,直到找到終止'\\0' 為什么是地址 65? 因為 65 是A的 ASCII 碼。 但是在內存中可能沒有從地址 65 開始的正確的、以空字符結尾的字符串; 實際上,您的程序很可能根本沒有從地址 65 讀取的權限。

那么你試過了

printf("%s\n", &c);    /* ALSO WRONG */

似乎有效。 它“有效”是因為,如果scanf 成功地將一個完整的字符串存儲到c並將未分配的內存放在它的右側,並且如果以某種方式破壞該內存不會導致(太多)其他問題,那么當您傳遞指針時&cprintf , printf 可以找到這些字符,組成一個字符串,並將它們打印出來。

所以它“有效”,但正如我所說,出於錯誤的原因:在這個過程中,它踩踏了它不“擁有”的所有內存,遲早,其他東西將因此不起作用。

你應該如何掃描和打印一個字符串? 一種方法是這樣的,正如我們之前看到的:

char s[10];
scanf("%s", s);

printf("%s\n", s);

現在,當scanf獲得指向數組s的第一個元素的指針時,它有 10 個字符可供使用。

我們確實必須擔心用戶鍵入超過 9 個字符的可能性。 但是有一個解決方法:我們可以告訴scanf一個字符串允許讀取多長時間,允許向我們傳遞給它的數組寫入多少個字符:

scanf("%9s", s);

那里的9告訴scanf不允許從用戶讀取超過 9 個字符。 由於 9 小於 10,因此仍有空間用於終止'\\0'字符。

關於scanf ,還有很多可以說的。 正如 chqrlie 在評論中指出的那樣,檢查它的返回值很重要,以確保它成功地轉換了你想要的盡可能多的值。 它有一些關於空格的奇怪規則。 除非您知道自己在做什么,否則不能將對scanf的調用與對其他輸入讀取函數(如getcharfgets調用混合在一起——您會得到奇怪的結果。 最后, scanf 非常挑剔,並且(最終)缺乏真正有用的功能,以至於根本不值得使用。 但這些是另一天的話題,因為這個答案已經是tl;dr

%s格式說明符需要一個指向字符串的指針。 當與scanf使用時,它必須是一個char數組,其中包含足夠的字符用於輸入的單詞加上指示字符串結尾的尾隨空字節。 printf()它必須是一個以空char結尾的char數組。

使用指向char變量的指針不起作用,因為它沒有空字節的空間。 您通過在變量之外寫入來導致未定義的行為。

char word[100];
scanf("%s", word);
printf("%s\n", word);

您可以使用%c來讀取和寫入單個字符而不是多個字符的字符串。

char letter;
scanf("%c", &letter);
printf("%c\n", letter);

在語句char a; a是字符變量 & 要掃描字符變量,請使用%c格式說明符。

scanf("%s",a);/* %s expects base address of char buffer, not single char */
scanf(" %c",&a);/* this is correct */

如果您想使用%s進行掃描,那么您的輸入應該是像char buf[10]這樣的char buf[10] 例如

char a[10];
scanf("%s",a);

當您使用 %c 或 %d 時,您不需要在 printf 中放置 &(&(&) 符號嗎? 無需向printf()提供地址& ,因為printf()作業是打印而不是掃描。 例如

char input;
scanf("%c",&input);/* here you need &, As scanf() will store input char into
                      address you provided i.e &input */
printf("%c",input);/*here no need &input, bcz input char already stored,
                     printf will just print the char*/

好吧,如果你打印地址,你可以使用%p

printf("%p",a);/*a is char buffer */

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM