2014年11月30日 星期日

C語言 printf() & scanf() 格式輸出輸入


學習C的過程中,通常是由終端機,也就是文字模式下開始,為了與程式互動,在終端機下輸出程式執行結果,或是從終端機取得使用者的輸入資料是基本需求,在C中標準輸入輸出是由stdio提供,這也就是為何您要在程式的一開頭終是加上:

#include <stdio.h>

將訊息輸出至終端機,稱之為「標準輸出」(Stand output),C藉由printf()將訊息輸出至終端機,我們已經看過幾個printf()函式的應用了,基本上,printf()就是將您指定的文字、數值等輸出至螢幕上,並且執行過後會傳回所輸出的字元數,例如:

#include <stdio.h>

int main(void) {
    int count = printf("This is a test!\n");
    printf("%d\n", count);
 
    return 0;
}

"This is a test!\n"當中包括換行字元,共有16個字元,因此count的值會是16,顯示結果如下:

This is a test!
16

標準輸出可以被重新導向至一個檔案,您可以在執行程式時使用" >>"將輸出結果導向至指定的檔案,例如(假設編譯後的可執行檔為main):

main >> result.txt

如果程式的目的是顯示"Hello! World!",則上面的執行會將結果導向至result.txt,而不會在螢幕上顯示"Hello! World!",result.txt中將會有輸出結果Hello! World!。要 重新導向 standard output 是用 '>', standard input 則是 "<",而">>" 除了重導 standard output 後,還有附加的功能,也就是會把輸出附加到被導向的目標檔案後頭,如果目標檔案本來不存在,那麼效果就和">"一樣。

如果在使用printf()時要指定整數、浮點數、字元等進行顯示,則要配合格式指定字(format specifier),以下列出幾個可用的格式指定碼:

%c以字元 方式輸出
%d10 進位整數輸出
%o以8進 位整數方式輸出
%u無號整數輸出
%x, %X將整 數以16進位方式輸出
%f浮點 數輸出
%e, %E使用科學記號顯示浮點數
%g, %G浮點數輸出,取%f或%e(%f或%E),看哪個表示精簡
%%顯示 %
%s字串輸出
















基本上,要顯示的是什麼資料型態,就必須搭配對應資料型態的格式指定字,但%d若用來輸出某個字元,將顯示其整數編碼值,若%c用來顯示某個整數,將顯示該整數對應編碼的字元,一個使用的範例如下所示:

#include <stdio.h>

int main(void) {
    printf("顯示字元 %c\n", 'A');
    printf("顯示字元編碼 %d\n", 'A');
    printf("顯示字元編碼 %c\n", 65);    
    printf("顯示十進位整數 %d\n", 15);
    printf("顯示八進位整數 %o\n", 15);
    printf("顯示十六進位整數 %X\n", 15);
    printf("顯示十六進位整數 %x\n", 15);    
    printf("顯示科學記號 %E\n", 0.001234);    
    printf("顯示科學記號 %e\n", 0.001234);    
   
    return 0;
}

顯示結果如下所示:

顯示字元 A
顯示字元編碼 65
顯示字元編碼 A
顯示十進位整數 15
顯示八進位整數 17
顯示十六進位整數 F
顯示十六進位整數 f
顯示科學記號 1.234000E-003
顯示科學記號 1.234000e-003

您可以在輸出浮點數時指定精度,例如若為浮點數:

printf("example:%.2f\n", 19.234);

.2指定小數點後取兩位,執行結果會輸出:

example:19.23

您也可以指定輸出時,至少要預留的字元寬度,無論是數值或字串,例如:

printf("example:%6.2f\n", 19.234);

整數6表示預留6個字元寬度,由於預留了6個字元寬度,不足的部份要由空白字元補上,所以執行結果會輸出如下(19.23只佔五個字元,所以補上一個空白在前端):

example: 19.23

若在%之後指定負號,例如%-6.2f,則表示靠左對齊,沒有指定則靠右對齊,例如:

#include <stdio.h>

int main(void) {
    printf("example:%6.2f\n", 19.234);
    printf("example:%-6.2f\n", 19.234);
    
    return 0;
}

顯示結果如下:

example: 19.23
example:19.23

若事先無法決定字元寬度,則可以使用*,例如:

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

int main(void) {
    printf("%*d\n", 1, 1);
    printf("%*d\n", 2, 1);
    printf("%*d\n", 3, 1);
    return 0;
}

printf()的*將被之後的第一個引數所取代,所以第一個printf()將預留一個字元寬度,第二個預留兩個字元寬度,第三個預留三個,顯示結果如下:

1
 1
  1

如果打算取得使用者的輸入,則可以使用「標準輸入」(Standard input)的scanf()函式,並搭配格式指定字與&取址運算子指定給變數,例如: 

#include <stdio.h>

int main(void) {
    int input;
    
    printf("請輸入數字:");
    scanf("%d", &input);
    printf("您輸入的數字:%d\n", input);
    
    return 0;
}

在程式中先宣告了一個整數變數input,使用scanf()函式時,若輸入的數值為整數,則使用格式指定字%d,若輸入的是 其它資料型態,則必須使用對應的格式指定字,如果是double,特別注意要使用%lf來指定,而您必須告知程式儲存資料的變數位址,為此,必須使用&取址運算子,這會將變數的記憶體位址取出,則 輸入的數值就知道變數的記憶體位址並儲存之(但字元陣列名稱本身就有位址資訊,故不用&來取址,之後說明陣列時會再看到)。

執行結果:

請輸入數字:10
您輸入的數字:10

scanf()在接受輸入時,可以接受多個值,也可以指定輸入的格式,例如:

#include <stdio.h>

int main(void) {
    int number1, number2;
    
    printf("請輸入兩個數字,中間使用空白區隔):");
    scanf("%d %d", &number1, &number2);
    printf("您輸入的數字:%d %d\n", number1, number2);
    
    printf("請再輸入兩個數字,中間使用-號區隔):");
    scanf("%d-%d", &number1, &number2);
    printf("您輸入的數字:%d-%d\n", number1, number2);
    
    return 0;
}

在第一個scanf()中,指定了使用空白來區隔兩個輸入,而第二個scanf()中,指定了使用-來區隔兩個輸入,一個執行與輸入的結果如下所示:

請輸入兩個數字,中間使用空白區隔):10 20
您輸入的數字:10 20
請再輸入兩個數字,中間使用-號區隔):30-40
您輸入的數字:30-40

scanf()還可以指定可接受的字元集合,例如若只想接受1到5的字元,則可以如下:

#include <stdio.h>

int main(void) {
    char str[50];
    
    printf("請輸入1到5的字元:");
    scanf("%[1-5]", str);
    printf("輸入的字元為 %s\n", str);

    fflush(stdin); // 清除輸入緩衝區

    printf("請輸入XYZ任一字元:");
    scanf("%[XYZ]", str);
    printf("輸入的字元為 %s\n", str);
    
    return 0;
}

str為C語言中的 
字串字 元陣 列scanf()函式連續讀入符合集合的字元並放到字元陣列中,直到讀到不符合的字元為止,剩下的字元仍會存在輸入緩衝區中,可以直接使用fflush(stdin)清除輸入緩衝區,以利進行下一次重新輸入,scanf()函式若成功,則傳回成功填寫的格式指定字數目,否則直接略過輸入而傳回0,在您學會流程控制語法之後,可以據以判斷該作什麼處理(像是使用if來針對不接受輸入時的處理)。

執行結果如下:

請輸入1到5的字元:146731
輸入的字元為 14
請輸入XYZ任一字元:XYZXDX
輸入的字元為 XYZX

您可以使用%[0-9]指定取得0至9的字元,使用%[A-z]指定取得ASCII表中的A到z的字元,如果要排除的話,則使用^,例如%[^ABC]會取得ABC字元以外的所有字元。

2014年11月29日 星期六

C語言資料型別

各種無符號類型量所占的記憶體空間位元組數與相應的有符號類型量相同。但由於省去了符號位元,故不能表示負數。
有符號整數變數:最大表示32767
0
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
無符號整數變數:最大表示65535
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1

    

C語言所定義的資料型別如下
        
型別符號位元位元長度表示方法數值範圍
整數16或32int-2147483648 ~ 2147483647
8char-128 ~ 127
16short-32768 ~ 32767
32long-2147483648 ~ 2147483647
64long long
16或32unsigned int0 ~ 4294967295
8unsigned char0 ~ 256
16unsigned short0 ~ 65535
32unsigned long0 ~ 4294967295
64unsigned long long
浮點數32float10^-38~10^38
64double10^-308~10^308
字元8char-128 ~ 127


浮點數(float)變數型別在電腦記憶體中通常都佔用4byte,也就是32bit。存放方式遵循IEEE-754格式標準[1]。此外,任何資料在記憶體中都是以二進制的形式存儲的。
在二進制科學表示法中,浮點數可表示成:S=M*2N,主要由三個部分構成:符號位元+階碼(N)+尾數(M)。對於float型別資料,其二進制有32位元,其中符號位元1個,階碼8個,尾數23個;對於double型別資料,其二進制為64位元,符號位1個,階碼11個,尾數52個。
浮點數float記憶體存放格式 
● 如果一个运算符两边的运算数类型不同,先要将其转换为相同的类型,即较低类型转换为较高类型,然后再参加运算,转换规则如下图所示。
double ←── float 高
long
unsigned
int ←── char,short 低
● 图中横向箭头表示必须的转换,如两个float型数参加运算,虽然它们类型相同,但仍要先转成double型再进行运算,结果亦为double型。 纵向箭头表示当运算符两边的运算数为不同类型时的转换,如一个long 型数据与一个int型数据一起运算,需要先将int型数据转换为long型, 然后两者再进行运算,结果为long型。所有这些转换都是由系统自动进行的, 使用时你只需从中了解结果的类型即可。这些转换可以说是自动的,当然,C语言也提供了以显式的形式强制转换类型的机制。

● 当较低类型的数据转换为较高类型时,一般只是形式上有所改变, 而不影响数据的实质内容, 而较高类型的数据转换为较低类型时则可能有些数据丢失。

ASCII 字碼表


ASCII CODE 0~127


C語言 --- 源码、反码、补码


          大家都知道数据在计算机中都是按字节来储存了,1个字节等于8位(1Byte=8bit),而计算机只能识别0和1这个数,所以根据排列,1个字节能代表256种不同的信息,即28(0和1两种可能,8位排列),比如定义一个字节大小的无符号整数(unsigned char),那么它能表示的是0~255(0~28-1)这些数,一共是256个数,因为,前面说了,一个字节只能表示256种不同的信息。别停下,还是一个字节的无符号整数,我们来进一步剖析它,0是这些数中最小的一个,我们先假设它在计算机内部就用8位二进制表示为00000000(从理论上来说也可以表示成其他不同的二进制码,只要这256个数每个数对应的二进制码都不相同就可以了),再假设1表示为00000001,2表示为00000010,3表示为00000011,依次类推,那么最大的那个数255在8位二进制中就表示为最大的数11111111,然后,我们把这些二进制码换算成十进制看看,会发现刚好和我们假设的数是相同的,而事实上,在计算机中,无符号的整数就是按这个原理来储存的,所以告诉你一个无符号的整数的二进制码,你就可以知道这个数是多少,而且知道在计算机中,这个数本身就是以这个二进制码来储存的。比如我给你一个2个字节大小的二进制码,首先声明它表示的是无符号的整数:00000000 00000010,我们把前面的0省略,换算一下,它表示的也是数值2,和前面不同的是,它占了2个字节的内存。不同的类型占的内存空间不同,如在我的电脑中char是1个字节,int是4个字节,long是8个字节(你的可能不同,这取决于不同的计算机设置),它们的不同之处仅仅是内存大的能表示的不同的信息多些,也就是能表示的数范围更大些(unsigned int能表示的范围是0~28*4-1),至于怎么算,其实都是一样的,直接把二进制与十进制相互转换,二进制就是它在计机机中的样子,十进制就是我们所表示的数(误解:不同的计算机储存的原理是不同的,取决于商家的喜好呢)。无符号的整数根本就没有原码、反码和补码。

  只有有符号的整数才有原码、反码和补码的!其他的类型一概没有。虽然我们也可以用二进制中最小的数去对应最小的负数,最大的也相对应,但是那样不科学,下面来说说科学的方法。还是说一个字节的整数,不过这次是有符号的啦,1个字节它不管怎么样还是只能表示256个数,因为有符号所以我们就把它表示成范围:-128-127。它在计算机中是怎么储存的呢?可以这样理解,用最高位表示符号位,如果是0表示正数,如果是1表示负数,剩下的7位用来储存数的绝对值的话,能表示27个数的绝对值,再考虑正负两种情况,27*2还是256个数。首先定义0在计算机中储存为00000000,对于正数我们依然可以像无符号数那样换算,从00000001到01111111依次表示1到127。那么这些数对应的二进制码就是这些数的原码。到这里很多人就会想,那负数是不是从10000001到11111111依次表示-1到-127,那你发现没有,如果这样的话那么一共就只有255个数了,因为10000000的情况没有考虑在内。实际上,10000000在计算机中表示最小的负整数,就是这里的-128,而且实际上并不是从10000001到11111111依次表示-1到-127,而是刚好相反的,从10000001到11111111依次表示-127到-1。负整数在计算机中是以补码形式储存的,补码是怎么样表示的呢,这里还要引入另一个概念——反码,所谓反码就是把负数的原码(负数的原码和和它的绝对值所对应的原码相同,简单的说就是绝对值相同的数原码相同)各个位按位取反,是1就换成0,是0就换成1,如-1的原码是00000001,和1的原码相同,那么-1的反码就是11111110,而补码就是在反码的基础上加1,即-1的补码是11111110+1=11111111,因此我们可以算出-1在计算机中是按11111111储存的。总结一下,计算机储存有符号的整数时,是用该整数的补码进行储存的,0的原码、补码都是0,正数的原码、补码可以特殊理解为相同,负数的补码是它的反码加1。下面再多举几个例子,来帮助大家理解!

十进制 → 二进制  (怎么算?)
47   → 101111

有符号的整数 原码       反码        补码
 47    00101111  00101111 00101111(正数补码和原码、反码相同,不能从字面理解)
-47    10101111  11010000 11010001(负数补码是在反码上加1,符号位不参与运算)



負數值 =   补码      轉      反码        +  1 

47       11010001      10101110  + 1  = 10101111