程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 對C說話編程尺度和聲明的根本懂得

對C說話編程尺度和聲明的根本懂得

編輯:關於C++

對C說話編程尺度和聲明的根本懂得。本站提示廣大學習愛好者:(對C說話編程尺度和聲明的根本懂得)文章只能為提供參考,不一定能成為您想要的結果。以下是對C說話編程尺度和聲明的根本懂得正文


c說話尺度
1978年,丹尼斯·裡奇(Dennis Ritchie)和Brian Kernighan協作出書了《C法式設計說話》的初版。書中引見的C說話尺度也被C說話法式設計師稱作“K&R C”,第二版的書中也包括了一些ANSI C的尺度。K&R C重要引見了以下特點:
構造(struct)類型
長整數(long int)類型
無符號整數(unsigned int)類型
把運算符=+和=-改成+=和-=。由於=+和=-會使得編譯器不曉得用戶要處置i = +10照樣i =- 10,使得處置上發生混雜。

即便在後來ANSI C尺度被提出的很多年後,K&R C依然是很多編譯器的最低尺度請求,很多老舊的編譯依然運轉K&R C的尺度。


C89

1989年,C說話被 ANSI 尺度化(ANSI X3.159-1989)。尺度化的一個目標是擴大K&R C。這個尺度包含了一些新特征。在K&R出書後,一些新特征被非官方地加到C說話中。
void 函數
函數前往 struct 或 union 類型
void * 數據類型

在 ANSI尺度化本身的進程中,一些新的特征被加了出來。ANSI也劃定一套了尺度函數庫。ANSI ISO(國際尺度化組織)成立 ISO/IEC JTC1/SC22/WG14 任務組,來劃定國際尺度的C說話。經由過程對ANSI尺度的大批修正,終究經由過程了 ISO 9899:1990。隨後,ISO尺度被 ANSI 采用。

傳統C說話到ANSI/ISO尺度C說話的改良包含:
增長了真實的尺度庫
新的預處置敕令與特征
函數原型許可在函數聲名中指定參數類型
一些新的症結字,包含 const、volatile 與 signed
寬字符、寬字符串與字節多字符
對商定規矩、聲明和類型檢討的很多小修改與廓清

WG14任務小組以後又於1995年,對1985年公布的尺度做了兩處技巧修訂(缺點修復)和一個彌補(擴大)。上面是 1995 年做出的一切修正:
3 個新的尺度庫頭文件 iso646.h、wctype.h 和 wchar.h
幾個新的記號與預界說宏,用於對國際化供給更好的支撐
printf/sprintf 函數一系列新的格局代碼
年夜量的函數和一些類型與常量,用於多字節字符和寬字節字符


C99

在 ANSI的尺度確立後,C說話的標准在一段時光內沒有年夜的更改,但是C++在本身的尺度化樹立進程中持續成長強大。《尺度修改案一》在1995年為C說話 樹立了一個新尺度,然則只修改了一些C89尺度中的細節和增長更多更廣得國際字符集支撐。不外,這個尺度引出了1999年ISO 9899:1999的揭橥。它平日被成為C99。C99被ANSI於2000年3月采取。

在C99中包含的特征有:
對編譯器限制增長了,好比源法式每行請求至多支撐到 4095 字節,變量名函數名的請求支撐到 63 字節(extern 請求支撐到 31)
預處置加強了。例如: 
宏支撐取可變參數 #define Macro(...) __VA_ARGS__
應用宏的時刻,參數假如不寫,宏裡用 #,## 如許的器械會擴大成空串。(之前會失足的)
支撐 // 行正文(這個特征現實上在C89的許多編譯器上曾經被支撐了)
增長了新症結字 restrict, inline, _Complex, _Imaginary, _Bool 
支撐 long long, long double _Complex, float _Complex 如許的類型
支撐 <: :> <% %> %: %:%: ,等等奇異的符號替換,D&E 裡提過這個
支撐了不定長的數組。數組的長度便可以用變量了。聲明類型的時刻呢,就用 int a[*] 如許的寫法。不外斟酌到效力和完成,這玩意其實不是一個新類型。所以就不克不及用在全局裡,或許 struct union 外面,假如你用了如許的器械,goto 語句就受限制了。
變量聲明不用放在語句塊的開首,for 語句倡導這麼寫 for(int i=0;i<100;++i) 就是說,int i 的聲明放在外面,i 只在 for 外面有用。
當一個相似構造的器械須要暫時結構的時刻,可以用(type_name){xx,xx,xx} 這有點像 C++ 的結構函數
初始化構造的時刻如今可以如許寫: 
struct {int a[3],b;} hehe[] = { [0].a = {1}, [1].a = 2 };
struct {int a, b, c, d;} hehe = { .a = 1, .c = 3, 4, .b = 5} // 3,4 是對 .c,.d 賦值的
字符串外面,/u 支撐 unicode 的字符
支撐 16 進制的浮點數的描寫
所以 printf scanf 的格局化串多支撐了 ll / LL(VC6 裡用的 I64)對應新的 long long 類型。
浮點數的外部數據描寫支撐了新尺度,這個可以用 #pragma 編譯器指定
除曾經有的 __line__ __file__ 之外,又支撐了一個 __func__ 可以獲得以後的函數名
關於異常數的表達式,也許可編譯器做化簡
修正了關於/% 處置正數上的界說,好比老的尺度裡 -22 / 7 = -3, -22 % 7 = -1 而如今 -22 / 7 = -4, -22 % 7 = 6
撤消了不寫函數前往類型默許就是 int 的劃定
許可 struct 界說的最初一個數組寫做 [] 不指定其長度描寫
const const int i;將被看成 const int i;處置
增 加和修正了一些尺度頭文件,好比界說 bool 的 <stdbool.h> 界說一些尺度長度的 int 的 <inttypes.h> 界說單數的 <complex.h> 界說寬字符的 <wctype.h> 有點泛型滋味的數學函數 <tgmath.h> 跟浮點數有關的 <fenv.h>。<stdarg.h> 裡多了一個 va_copy 可以復制 ... 的參數。<time.h> 裡多了個 struct tmx 對 struct tm 做了擴大
輸出輸入對寬字符還有長整數等做了響應的支撐

 

絕對於c89的變更還有

  1、增長restrict指針

  C99中增長了公實用於指針的restrict類型潤飾符,它是初始拜訪指針所指對象的唯一門路,是以只要借助restrict指針表達式能力 拜訪對象。restrict指針指針重要用做函數變元,或許指向由malloc()函數所分派的內存變量。restrict數據類型不轉變法式的語義。

  假如某個函數界說了兩個restrict指針變元,編譯法式就假定它們指向兩個分歧的對象,memcpy()函數就是restrict指針的一個典范運用示例。C89中memcpy()函數原型以下:

  代碼: void *memcpy (void *s1, const void *s2, size_t size);

  假如s1和s2所指向的對象堆疊,其操作就是不決義的。memcpy()函數只能用於不堆疊的對象。C99中memcpy()函數原型以下:代 碼: void *memcpy(void *restrict s1, const void *restrict s2,size_t size);

  經由過程應用restrict潤飾s1和s2 變元,可確保它們在該原型中指向分歧的對象。

  2、inline(內聯)症結字

  內聯函數除堅持構造化和函數式的界說方法外,還能使法式員寫出高效力的代碼。函數的每次挪用與前往都邑消費相當年夜的體系資本,特別是當函數調 用產生在反復次數許多的輪回語句中時。普通情形下,當產生一次函數挪用時,變元須要進棧,各類存放器內存須要保留。當函數前往時,存放器的內容須要恢復。 假如該函數在代碼內停止聯機擴大,現代碼履行時,這些保留和恢復操作旅游運動會再產生,並且函數挪用的履行速度也會年夜年夜加速。函數的聯機擴大會發生較長的 代碼,所以只應當內聯對運用法式機能有明顯影響的函數和長度較短的函數。

  3、新增數據類型

  _Bool

  值是0或1。C99中增長了用來界說bool、true和false宏的頭文件夾<stdbool.h>,以便法式員可以或許編寫同時兼容於C與C++的運用法式。在編寫新的運用法式時,應當應用

  <stdbool.h>頭文件中的bool宏。

  _Complex and _Imaginary

  C99尺度中界說的單數類型以下:float_Complex;float_Imaginary;double_Complex;double_Imaginary;long double_Complex;long double_Imaginary。

  <complex.h>頭文件中界說了complex和imaginary宏,並將它們擴大為_Complex和 _Imaginary,是以在編寫新的運用法式時,應當應用<stdbool.h>頭文件中的complex和imaginary宏。

  long long int

  C99尺度中引進了long long int(-(2e63 - 1)至2e63 - 1)和unsigned long long int(0 - 2e64 - 1)。long long int可以或許支撐的整數長度為64位。

  4、對數組的加強

  可變長數組

  C99中,法式員聲明數組時,數組的維數可以由任一有用的整型表達式肯定,包含只在運轉時能力肯定其值的表達式,這類數組就叫做可變長數組,然則只要部分數組才可所以變長的。

  可變長數組的維數在數組生計期內是不變的,也就是說,可變長數組不是靜態的。可以變更的只是數組的年夜小。可使用*來界說不肯定長的可變長數組。

  數組聲明中的類型潤飾符

  在C99中,假如須要應用數組作為函數變元,可以在數組聲明的方括號內應用static症結字,這相當於告知編譯法式,變元所指向的數組將至多 包括指定的元素個數。也能夠在數組聲明的方括號內應用restrict,volatile,const症結字,但只用於函數變元。假如應用 restrict,指針是初始拜訪該對象的唯一門路。假如應用const,指針一直指向統一個數組。應用volatile沒有任何意義。

  5、單行正文

  引入了單行正文標志 "//" , 可以像C++一樣應用這類正文了。

  6、疏散代碼與聲明

  7、預處置法式的修正

  a、變元列表

  宏可以帶變元,在宏界說頂用省略號(...)表現。外部預處置標識符__VA_ARGS__決議變元將在何處獲得調換。例:#define MySum(...) sum(__VA_ARGS__) 語句MySum(k,m,n);

  將被轉換成:sum(k, m, n); 變元還可以包括變元。例: #define compare(compf, ...) compf(__VA_ARGS__) 個中的compare(strcmp,"small", "large"); 將調換成:strcmp("small","large");

  b、_Pragma運算符

  C99引入了在法式中界說編譯指令的別的一種辦法:_Pragma運算符。格局以下:

  _Pragma("directive")

  個中directive是要滿打滿算的編譯指令。_Pragma運算符許可編譯指令介入宏調換。

  c、外部編譯指令

  STDCFP_CONTRACT ON/OFF/DEFAULT 若為ON,浮點表達式被當作基於硬件方法處置的自力單位。默許值是界說的對象。

  STDCFEVN_ACCESS ON/OFF/DEFAULT 告知編譯法式可以拜訪浮點情況。默許值是界說的對象。

  STDC CX_LIMITED_RANGE ON/OFF/DEFAULT 若值為ON,相當於告知編譯法式某法式某些含有單數的公式是靠得住的。默許是OFF。

  d、新增的外部宏

  __STDC_HOSTED__ 若操作體系存在,則為1

  __STDC_VERSION__ 199991L或更高。代表C的版本

  __STDC_IEC_599__ 若支撐IEC 60559浮點運算,則為1

  __STDC_IEC_599_COMPLEX__ 若支撐IEC 60599單數運算,則為1

  __STDC_ISO_10646__ 由編譯法式支撐,用於解釋ISO/IEC 10646尺度的年和月格局:yyymmmL

  9、復合賦值

  C99中,復合賦值中,可以指定對象類型的數組、構造或結合表達式。當應用復合賦值時,應在括弧內指定類型,後跟由花括號圍起來的初始化列表;若類型為數組,則不克不及指定命組的年夜小。建成的對象是未定名的。

  例: double *fp = (double[]) {1.1, 2.2, 3.3};

  該語句用於樹立一個指向double的指針fp,且該指針指向這個3元素數組的第一個元素。 在文件域內樹立的復合賦值只在法式的全部生計期內有用。在模塊內樹立的復合賦值是部分對象,在加入模塊後不再存在。

  10、柔性數組構造成員

  C99中,構造中的最初一個元素許可是未知年夜小的數組,這就叫做柔性數構成員,但構造中的柔性數構成員後面必需至多一個其他成員。柔性數構成員 許可構造中包括一個年夜小可變的數組。sizeof前往的這類構造年夜小不包含柔性數組的內存。包括柔性數構成員的構造用malloc()函數停止內存的靜態 分派,而且分派的內存應當年夜於構造的年夜小,以順應柔性數組的預期年夜小。

  11、指定的初始化符

  C99中,該特征對常常應用稀少數組的法式員非常有效。指定的初始化符平日有兩種用法:用於數組,和用於構造和結合。用於數組的格局: = vol; 個中,index表現數組的下標,vol表現本數組元素的初始化值。

  例如: int x[10] = {[0] = 10, [5] = 30}; 個中只要x[0]和x[5]獲得了初始化。用於構造或結合的格局以下:

  member-name(成員稱號)

  對構造停止指定的初始化時,許可采取簡略的辦法對構造中的指定成員停止初始化。

  例如: struct example{ int k, m, n; } object = {m = 10,n = 200};

  個中,沒有初始化k。對構造成員停止初始化的次序沒無限制。

  12、printf()和scanf()函數系列的加強

  C99中printf()和scanf()函數系列引進了處置long long int和unsigned long long int數據類型的特征。long long int 類型的格局潤飾符是ll。在printf()和scanf()函數中,ll實用於d,i,o,u和x格局解釋符。別的,C99還引進了hh潤飾符。當應用 d,i,o,u和x格局解釋符時,hh用於指定char型變元。ll和hh潤飾符都可以用於n解釋符。

  格局潤飾符a和A用在printf()函數中時,成果將會輸入十六進制的浮點數。格局以下:[-]0xh, hhhhp + d 應用A格局潤飾符時,x和p必需是年夜寫。A和a格局潤飾符也能夠用在scanf()函數中,用於讀取浮點數。挪用printf()函數時,許可在%f解釋 符前加上l潤飾符,即%lf,但不起感化。

  13、C99新增的庫

  C89中尺度的頭文件

  <assert.h> 界說宏assert()

  <ctype.h> 字符處置

  <errno.h> 毛病申報

  <float.h> 界說與完成相干的浮點值

  <limits.h> 界說與完成相干的各類極限值

  <locale.h> 支撐函數setlocale()

  <math.h> 數學函數庫應用的各類界說

  <setjmp.h> 支撐非部分跳轉

  <signal.h> 界說旌旗燈號值

  <stdarg.h> 支撐可變長度的變元列表

  <stddef.h> 界說經常使用常數

  <stdio.h> 支撐文件輸出和輸入

  <stdlib.h> 其他各類聲明

  <string.h> 支撐串函數

  <time.h> 支撐體系時光函數

  C99新增的頭文件和庫

  <complex.h> 支撐單數算法

  <fenv.h> 給出對浮點狀況標志和浮點情況的其他方面的拜訪

  <inttypes.h> 界說尺度的、可移植的整型類型聚集。也支撐處置最年夜寬度整數的函數

  <iso646.h> 起首在此1995年第一次修訂時引進,用於界說對應各類運算符的宏

  <stdbool.h> 支撐布爾數據類型類型。界說宏bool,以便兼容於C++

  <stdint.h> 界說尺度的、可移植的整型類型聚集。該文件包括在<inttypes.h>中

  <tgmath.h> 界說普通類型的浮點宏

  <wchar.h> 起首在1995年第一次修訂時引進,用於支撐多字節和寬字節函數

  <wctype.h> 起首在1995年第一次修訂時引進,用於支撐多字節和寬字節分類函數

  14、__func__預界說標識符

  用於指出__func__所寄存的函數名,相似於字符串賦值。

  15、其它特征的修改

  放寬的轉換限制

不再支撐隱含式的int規矩

  刪除隱含式函數聲明

  對前往值的束縛

  C99中,非空類型函數必需應用帶前往值的return語句。

  擴大的整數類型

對整數類型晉升規矩的改良

  C89中,表達式中類型為char,short int或int的值可以晉升為int或unsigned int類型。

  C99中,每種整數類型都有一個級別。例如:long long int 的級別高於int,int的級別高於char等。在表達式中,其級別低於int或unsigned int的任何整數類型都可被調換成int或unsigned int類型。

 

然則各個公司對C99的支撐所表示出來的興致分歧。當GCC和其它一些貿易編譯器支撐C99的年夜部門特征的時刻,微軟和Borland卻仿佛對此不感興致。

 

GCC 支撐C99,經由過程 --std = c99敕令行參數開啟。 例如:gcc --std = c99 test.c。默許情形下GCC是用的GNU89尺度

C說話聲明
優先級規矩剖析
A.聲明從他的第一個標識符(名字)開端讀取,然後依照優先級次序順次讀取:
B 優先級從高到低順次是:
    B.1聲明中被括號括起來的那部門
    B.2後綴操作符:
          括號()表現這是一個函數,而方括號[]表現這是一個數組。
    B.3前綴操作符:星號*表現“指向...的指針”
C 假如const和(或)volatile症結字的前面緊跟類型解釋符(如int,long等),那末它用做子類型解釋符。在其他情形下,    const和(或)volatile症結字感化於它右邊的鄰近的指針星號。
例:

  char * const *(*next)();

A             起首,看變量名(標識符)“next”,並留意到它直接被括號所括住
B.1          所以先把括號裡的器械作為一個全體,得出“next是一個指向...的指針”
B             然後斟酌括號裡面的器械,在星號前綴和括號後綴之間作出選擇
B.2          B.2規矩告知我們優先級較高的是左邊的函數括號,所以得出“next是一個函數指針,指向一個前往...的函數”
B.3          然後,處置前綴“*”,得出指針所指的內容
C             最初,把“char * const”說明為指向字符的常量指針

 

更直不雅一點可以看下圖

所以這個聲明表現“next”是一個指針,它指向一個函數,該函數前往另外一個指針,該指針指向一個類型為char的常量指針。


例子2  char *(* c[10]) (int **p)

那末合起來就是c是一個函數指針數組,他的前往類型是char *,參數是int **p

 

以下是來自c專家編程的一個剖析聲明的法式

 

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAXTOKENS 100
#define MAXTOKENLEN 64

enum type_tag { IDENTIFIER, QUALIFIER, TYPE};

struct token{
 char type;
 char string[MAXTOKENLEN];
};

int top = -1;
struct token stack[MAXTOKENS];
struct token this;

#define pop stack[top--]
#define push(s) stack[++top] = s

enum type_tag classify_string(void)
{
    char *s = this.string;
    if ( !strcmp(s,"const"))
    {
        strcpy(s,"read-only");
        return QUALIFIER;
    }
    if (!strcmp(s,"volatile"))
        return QUALIFIER;
    if (!strcmp(s,"void"))
        return TYPE;
    if (!strcmp(s,"char"))
        return TYPE;
    if (!strcmp(s,"signed"))
        return TYPE;
    if (!strcmp(s,"unsigned"))
        return TYPE;
    if (!strcmp(s,"short"))
        return TYPE;
    if (!strcmp(s,"int"))
        return TYPE;
    if (!strcmp(s,"void"))
        return TYPE;
    if (!strcmp(s,"long"))
        return TYPE;
    if (!strcmp(s,"float"))
        return TYPE;
    if (!strcmp(s,"double"))
        return TYPE;
    if (!strcmp(s,"struct"))
        return TYPE;
    if (!strcmp(s,"union"))
        return TYPE;
    if (!strcmp(s,"enum"))
        return TYPE;
    return IDENTIFIER;
}

void gettoken(void)
{
    char *p = this.string;

    while((*p = getchar() ) == ' ');

    if ( isalnum(*p) )
    {
        while( isalnum( *++p = getchar() ));
        ungetc(*p , stdin);
        *p = '/0';
        this.type = classify_string();
        return;
    }

    if (*p == '*' )
    {
        strcpy(this.string, "pointer to");
        this.type = '*';
        return;
    }

    this.string[1] = '/0';
    this.type = *p;
    return;
}

read_to_first_identifer()
{
    gettoken();
    while(this.type != IDENTIFIER )
    {
        push( this );
        gettoken();
    }
    printf("%s is ",this.string);
    gettoken();
}

deal_with_arrays()
{
    while(this.type = '[' )
    {
        printf("array");
        gettoken();
        if ( isdigit(this.string[0]) )
        {
            printf("0..%d ",atoi(this.string)-1);
            gettoken();
        }
        gettoken();
        printf("of ");
    }
}

deal_with_function_args()
{
    while( this.type != ')' )
    {
        gettoken();
    }
    gettoken();
    printf("function returning ");
}

deal_with_pointers()
{
    while(stack[top].type == '*' )
    {
        printf("%s ", pop.string);
    }
}

deal_with_declarator()
{
    switch(this.type)
    {
    case '[' :deal_with_arrays();break;
    case '(' :deal_with_function_args();
    }

    deal_with_pointers();

    while( top >= 0 )
    {
        if ( stack[top].type == '(' )
        {
            pop;
            gettoken();
            deal_with_declarator();
        }
        else
            printf("%s ",pop.string);
    }
}

main()
{
    read_to_first_identifer();
    deal_with_declarator();
    printf("/n");
    return 0;

}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved