2015年6月7日 星期日

[C] %s 的使用



當使用 %s 於 printf 和 scanf 時,  對應的argument 必須是 char * , 如下列例子:

char *str_constant = "I point to a string literal";
char str_buf[] = "I am an array of char initialized with a string literal";

printf("string literal = %s\n", "I am a string literal");
printf("str_constant = %s\n", str_constant);
printf("str_buf = %s\n", str_buf);

scanf("%55s", str_buf);

值得注意的是, 看以下小程序,

#define  max  5
----------------------
        char *name[max];

int i;
for (i = 0; i < max; i++){
name[i] = malloc(3);
scanf("%3s", name[i]);
printf("%s\n", name[i]);
                free(name[i]);
}

// 使用scanf前必須用使用 malloc 為每個char * pointer 動態分配位置記存字符
// %3s , 即指只讀取讀入字串的前3個字符





2015年6月1日 星期一

[C] auto 與 register

C語言變數儲存等級(storage class),一共有四種:auto、extern、register、static,我們最常用的是auto,然而卻很少人用它來宣告變數,怎麼說起來很奇怪?因為編譯器自動把我們宣告的變數自動設成auto,只是我們都忽略有這回事。看個例子:
1
2
int i;
auto int i;
以上兩者的意義相同,auto變數在程式執行的時候,OS配置記憶體空間給這個變數。
現在我們來討論register變數,跟auto變數一樣,register變數在程式執行的時候,OS會配置記憶體空間給此變數,但跟auto變數不一樣的是,register變數會被配置到CPU中的暫存器(register),而auto變數會被配置到RAM中,它們的差異在於存取速度,前者(register)快於後者(RAM),那到底快多少呢?我看到網路上都說「快」,但到底有多快?我們實際來測試看看吧~
1
for(register int i=0;i<1000000000;i++);
我舉我們最常使用的for迴圈,讓它空轉10的9次方次,我的電腦執行時間為:
2.98905059209784340000
請按任意鍵繼續 . . .
而去掉register宣告,結果則為:
3.03692563877645630000
請按任意鍵繼續 . . .
雖然看起來只有差0.05秒左右,但你若宣告long long的話,差異就更明顯了。不過當今電腦速度這麼快,誰會在乎這麼一點時間呢?
結論就是驗證了儲存裝置的層級:register存取速度優於RAM。前者量少而貴但相對較快,後者則相反。

文章出處 : https://cg2010studio.wordpress.com/2011/04/08/register-variable/

[C] static 與 extern



static變數,當變數有宣告時加上static限定時,一但變數生成,它就會一直存在記憶體之中,即使函式執行完畢,變數也不會消失,例如: 


#include <stdio.h>

void count(void);

int main(void) {
    int i;
    for(i = 0; i < 10; i++) {
        count();     
    }

    return 0;
}

void count(void) { 
    static int c = 1; 
    printf("%d\\n", c); 
    c++; 
}

執行結果:
1
2
3
4
5
6
7
8
9
10

雖然變數c是在count()函式中宣告的,但是函式結束後,變數仍然存在,它會直到程式執行結束時才消失,雖然變數一直存在,但由於它是被宣告在函式之 中,所以函式之外仍無法存取static變數。 

您可以宣告全域static變數,其在程式執行期間一直存在,但在一個原始程式文件中宣告全域static變數,還表示其可以存取的範圍僅限於該原始程式文件之中,您也可以將函式宣告為static:
static void some() {
    ...
}

一個static函式表示,其可以呼叫的範圍限於該原始碼文件之中,如果您有一些函式僅想在該原始程式文件之中使用,則可以宣告為static,這也可以避免與其他人寫的函式名稱衝突的問題。

extern可以聲明變數會在其它的位置被定義,這個位置可能是在同一份文件之中,或是在其它文件之中,例如: 
  • some.c
double someVar = 1000;
// 其它定義 ...


  • main.c
#include <stdio.h>

int main(void) {
    extern double someVar;
    
    printf("%f\\n", someVar);
    
    return 0;
}

在main.c中實際上並沒有宣告someVar,extern指出someVar是在其它位置被定義,編譯器會試圖在其它位置或文件中找出 someVar的定義,結果在some.c中找到,因而會顯示結果為1000,要注意的是,extern聲明someVar在其它位置被定義,如果您 在使用extern時同時指定其值,則視為在該位置定義變數,結果就引發重覆定義錯誤,例如:
#include <stdio.h>

int main(void) {
  extern double someVar = 100;
 // error, `someVar' has both `extern' and initializer     
    ...
    return 0;
}

您必須先聲明extern找到變數,再重新指定其值,這麼使用才是正確的:


#include <stdio.h>

int main(void) {
    extern double someVar;
    
someVar = 100;
    ...
    return 0;
}

文章出處 :http://openhome.cc/Gossip/CGossip/Scope.html

[C] 可視範圍(scope)


在C中,談到可視範圍(scope)可分為許多層次,也可以談到很複雜,在這邊先談談

-「全域變數」(Global variable)
-「區域變數」(Local variable)
-「區塊變數」(Block variable)。 

全域變數是指直接宣告在(主)函式之外的變數,這個變數在整個程式之中都「看」得它的存在,而可以呼叫使用,例如:
const double PI = 3.14159; 

doule area(double r) { 
    return r * r * PI; 
}

int main(void) { 
    // ..... 
    return 0; 

在這個例子中,PI這個變數可以被主函式main()與副函式area()來使用,通常全域變數是用來定義一些常數,初學者不應為了方便而將所有的變數都 設定為全域變數,否則將來一定會發生變數名稱管理上的問題,全域變數的生命週期始於程式開始之時,終止於程式結束之時。 

區域變數是指宣告在函式之內的變數,或是宣告在參數列之前的變數,它的可視範圍只在宣告它的函式區塊之中,其它的函式不可以使用該變數,例如在上例的主函 式中,您不可以直接對area()函式中的變數r作出存取的動作,區域變數的生命週期開始於函式被呼叫之後,終止於函式執行完畢之時。 

區塊變數是指宣告在某個陳述區塊之中的變數,例如while迴圈區塊中,或是for迴圈區塊,例如下面的變數i在迴圈結束之後,就會自動消失:
while(...) {
    int i = 0;
    // ....
}

當一可視範圍大的變數與可視範圍小的變數發生同名狀況時,可視範圍小的變數會暫時覆蓋可視範圍大的變數,稱之為「變數覆蓋」,例如: 
int num = 10;
int i;
for(i = 0; i < 100; i++)  {
    int num = 20;
    // ...
}
printf("%d", num);

這個程式最後顯示的 num 值仍是10,當執行迴圈時,迴圈內的 num 變數作用將覆蓋迴圈外的 num 變數;同樣的作用發生於全域變數與區域變數發生同名的時候。 



文章來源:http://openhome.cc/Gossip/CGossip/Scope.html

[C] 列舉(enum)


  • 列舉型態(enumeration)是一種特殊的常數定義方式。
  • 列舉型態可使我們定義自己的資料型態,及設定其值。
  • 程式的可讀性提高。
  • 列舉型態的定義及宣告方式格式如下:


定義1:

  1. enum color
  2. {
  3.   red,
  4.   blue,
  5.   green,
  6. };
  7. enum color coat, hat;      


定義2:
  1. enum color        /*宣告列舉型態color */
  2. {
  3.   red,
  4.   blue,
  5.   green,
  6. } coat , hat; 


定義3:

使用typedef

  1. typedef enum{
  2.   All,
  3.   January,
  4.   February,
  5.   March,
  6.   April,
  7.   May
  8. }month;

  9. month Month;


============================================================

Example 1 :




===================================================================

Remark:

enum 是英文 enumerate(列舉,枚舉) 的縮寫
效果是列舉出多個'常數',並設定數值。
例子:
  enum{
    stop,   
    stand,
    
    run
  };
大括號中每一個元素稱為列舉元,每個列舉元其實儲存著一個整數
如果沒設定數值,預設是從0開始遞增。(stop=0 ; stand=1 ; run=2)
如果要設定數值,可以這樣打
  enum{
    stop=5,  
    stand,
 
    run
 };
這樣就會變成 (stop=5 ; stand=6 ; run=7)
也可以這樣用
  enum{
    stop=5,
    stand,
    run=2
  };
這樣會變成 (stop=5 ; stand=6 ; run=2)

參考網址: http://bodscar.pixnet.net/blog/post/61204511-%E8%AA%AA%E6%98%8E-typedef-enum

[C] Typedef

typedef 用來對一個資料類型取一個新名字。目的是為了讓程式碼更好讀更好理解
舉個例子: 
我們來為int取個可愛的名字 這樣看起來比較順眼
未使用 typedef 的樣貌
int color;  //宣告color 為一變數
int leaf;    // 宣告leaf 為一變數
...
...
...
使用 typedef 後的樣貌 ->
typedef int Apple;      // int 的名子為Apple(蘋果)
typedef int Orange;       // int 的名字Orange(橘子)
Apple color;         //宣告color 為一變數
Orange leaf;       // 宣告leaf 為一變數
...
...
...
另一個更常見的例子是用在幫結構(struct)取名子
未使用 typedef 的樣貌
struct var {
    int math;
    int english;
    char data3;
};
struct var yo;
這個結構以往在C當中要使用就要用這樣的方式

struct var yo;
但是只要使用 typedef 就能改名了   如下   
 typedef struct var results;
 這樣之後要使用時就能改成這樣

results yo;

完整用法
struct var {
    int math;
    int english;
    char data3;
};
typedef struct var results; 
results yo;


進階用法
typedef struct {
      int math;
      int english;
      char data3;
} result;

result yo;  // 直接用取


文章出處 : http://bodscar.pixnet.net/blog/post/61204511-%E8%AA%AA%E6%98%8E-typedef-enum


[C] Pointers on C - note 1

/********************************************************************/


1.
    return Exit_SUCCESS;  ==  return 0;   //  program hasn't error.
    return Exit_FAILURE;   // program is having error.
 


/********************************************************************/

2.
    char input [100];
    gets(intput);   // 同scanf一樣道理, 但scanf不食空白, gets會食埋空白!



/********************************************************************/



3.
   while( num < max && scanf( "%d", &columns[num] ) == 1
    &&columns[num] >= 0 )

    scanf( "%d", &columns[num] ) == 1
    //  scanf 第2個參數needs address, 完成後會return返總共讀取了多少個值
    //  == 1 即確保讀入的總量是1 , -1 即讀取失敗



/********************************************************************/



4.

    puts( "Last column number is not paired." ); // 同printf同樣道理,但只會輸出文字, 不能包括變量
   exit( EXIT_FAILURE );  // 即時結束程序




/********************************************************************/



5.
while( (ch = getchar()) != EOF && ch != '\n' )
   // 主要用以識別'\n' , 即enter
   // getchar() 可以每次取讀一個字符
   // EOF == -1 , 指完成輸入



/********************************************************************/


 
6.

char *output;
char const *input;    // const == 常數, 不能在程序中修改其值


strncpy( output , input, 10);


// char * strncpy ( char * destination, const char * source, size_t num );
// 把參數2的參數3個字符抄寫到參數1中




/********************************************************************/



7.
    Here is a better way to logically delete code in a C program
 
    #if 0
              statements
    #endif   



/********************************************************************/



8.

   #include <stdio.h>
   #define MAX_COLS       20

   These two lines are Preprocessor Directive(前置指令)




/********************************************************************/


9.

P. 40/609

There are only four basic data types in C – 

 1. integers, 
 2. floating‐point values, 
 3. pointers,
 4. aggregate types such as arrays and structures.

 All other types are derived from some combination of these four.


   


 *//********************************************************************/


10.

P. 43/609

Integers can be given in octal (8進制) by starting with the digit zero,
or in hexadecimal by starting with 0x, as in

0173 0177777 000060   // octal

0x7b 0xFFFF 0xabcdef00  // hexadecimal





/********************************************************************/


/********************************************************************/

11.

typedef char *ptr_to_char;
ptr_tochar a;


/********************************************************************/

12.

int *pi;
// pi is an ordinary pointer to an integer.

int const *pci;
// pci is a pointer to a constant integer. You can change the pointer’s value but not the
value to which it points.


int * const cpi;
// which is a constant pointer to an integer. Here the pointer is the constant; its value
cannot change, but you are free to modify the integer to which it points.
// cpi不可再指其他integer address,但可以修改其指緊的integer的value


int const * const cpci
// Finally, there is cpci; in this example both the pointer and the value to which it points
are constant;



/********************************************************************/


13.

P.86

There is one situation in which many claim that a goto might be
appropriate in a well structured program—breaking out of nested loops.

Because the break statement only affects the innermost loop that encloses it, the only way to
immediately exit a deeply nested set of loops is with a goto, as shown in this example.


  1. while( condition1 ){
  2. while( condition2 ){
  3. while( condition3 ){
  4. if( some disaster )
  5. goto quit;
  6. }
  7. }
  8. }
  9. quit: ;


There are two alternatives to using a goto. First, a status flag can be set when
you want to exit all of the loops, but the flag must then be tested in every loop:

  1. enum { EXIT, OK } status;
  2. ...
  3. status = OK;
  4. while( status == OK && condition1 ){
  5. while( status == OK && condition2 ){
  6. while( condition3 ){
  7. if( some disaster ){
  8. status = EXIT;
  9. break;
  10. }
  11. }
  12. }
  13. }


/********************************************************************/



14.

% is the modulo operator


/********************************************************************/



15.

P.95

shift simply slides the bits in a value to the
left or the right. On a left shift, bits on the left side of the value are discarded, and zero
bits are put into the space created on the right side by the shift,

The left shift operator is << and the right shift operator is >>.


/********************************************************************/



16.

P. 98


a = x = y + 3;

The assignment operator associates (is evaluated) from right to left, so this statement is
equivalent to:

a = ( x = y + 3 );


/********************************************************************/



17.

P.101

There are a number of unary(一元) operators, that is, operators that take only one operand.

They are

!   ++   -   &    sizeof     ~   --   +   *   (type)


/********************************************************************/



18.

The sizeof operator determines the size of its operand, measured in bytes

sizeof(int) // returns the number of bytes in an integer variable == 4bytes == 32bits



/********************************************************************/



19.

P.110

C does not have an explicit Boolean type so integers are used instead. The rule is.
Zero is false, and any nonzero value is true



/********************************************************************/




20.

P.111

Here is another shortcut that programmers often use with if statements – one in
which this same kind of trouble can occur. Assuming that you have made the
following #defineʹs, then each of the pairs of statements below seem equivalent.

#define FALSE 0
#define TRUE 1
...
if( flag == FALSE ) ...
if( !flag ) ...



/********************************************************************/




21.

P.112

An L‐value is something that can appear on the left side of an equal sign (L for
left). An R‐value is something that can appear on the right side of an equal sign. Here is
an example:

a = b + 25;

a is an L‐value because it identifies a place where a result can be stored. b + 25 is an R-value
because it designates a value.



/********************************************************************/




22.

P.114

This fragment of code contains a potential problem.


  1. int a = 5000;
  2. int b = 25;
  3. long c = a * b;


The problem is that the expression a * b is evaluated using integer arithmetic.
This code works fine on machines with 32‐bit integers, but the multiplication overflows on
machines with 16‐bit integers, so c is initialized to the wrong value.


The solution is to convert one (or both) of the values to a long before the
multiplication.


  1. long c = (long)a * b;


It is possible to lose precision when converting an integer to a float. Floating
values are only required to have six decimal digits of precision; if an integer that is
longer than six digits is assigned to a float, the result may be only an approximation of
the integer value.



/********************************************************************/



23.

P.118


  1. /*
  2. ** A program to demonstrate that the order of expression evaluation
  3. ** is only partially determined by operator precedence.
  4. */
  5. main()
  6. {
  7. int i = 10;
  8. i = i-- - --i * ( i = -3 ) * i++ + ++i;
  9. printf( "i = %d\n", i );
  10. }



// output : 4
// 由於以上程序是illegal, 所以不同compiler會出現不同的output







 














/********************************************************************/



24.

P. 120

+ (addition)
- (subtraction)
* (multiplication)
 / (division)
 % (modulo)

<< and >> operators perform left and right shifts,



/********************************************************************/




25

P.130

On many modern machines, each byte contains eight bits,
which can store unsigned integers from 0 to 255 or signed integers from ‐128 to 127.




/********************************************************************/



26.


*(int *)100 = 25;
The cast converts the value 100 from an ʺintegerʺ to a ʺpointer to an integer.ʺ




/********************************************************************/



27.

P.141

Pointer to pointer

int a = 12;
int *b = &a;
int **c = &b;

It is legal, pointer c now is pointing to the location of integer variable a.
int **c = &b;  // this expression is equivalent to *(*c).


/********************************************************************/



28.

P. 145

*cp + 1
// * has a higher precedence than +
// A copy of this value is taken and added to one, giving the character 'b' as a
result
// The final result does not reside in any identifiable location, so this expression is not a legal
L‐value.



/********************************************************************/


29.

P. 146

*cp++  // First * , then ++
*++cp  // First ++, then *
++*cp  // First * , then ++


Example :


Given :

  1. int main(){

  2. int a = 10;
  3. *(&a + 1) = 999;
  4. int *p = &a;

  5. // each cases
  6.         // ................

  7. system("pause");
  8. return 0;

  9. }


/* case 1 */
printf("%d", *p++);
> output : 10

/* case 2 */
printf("%d", *++p);
> output : 999

/* case 3 */
printf("%d", ++*p);
> output : 11

/* case 4 */
printf("%d", (*p)++);
> output : 10

/* case 5 */
printf("%d", ++*++p);
> output : 1000

/* case 6 */
printf("%d", ++*p++);
> output : 11



/********************************************************************/




30.

P. 149

Here are some sample programs to illustrate a few common pointer expressions.
Program 6.1 computes the length of a string. You should never have to write this
function because the library contains one, but it is a useful example.

  1. /*
  2. ** Compute the length of a string.
  3. */
  4. #include <stdlib.h>
  5. size_t
  6. strlen( char *string )
  7. {
  8. int length = 0;
  9. /*
  10. ** Advance through the string, counting characters
  11. ** until the terminating NUL byte is reached.
  12. */
  13. while( *string++ != '\0' )
  14. length += 1;
  15. return length;
  16. }




/********************************************************************/


31.

P. 156


int pointer - int pointer 的情況,

For example, if p1 points to array[i] and p2 points to
array[j] then p2 – p1 will have the same value as j – i.

例如 array[i] location = 1000,  array[j] location = 1024,
p2 - p1 =  6

* int = 32bit = 4bytes



/********************************************************************/

32.

P.157

Relational operations on pointers are also constrained. It is possible to compare two
pointer values with the relational operators
< <= > >=



  1. #define N_VALUES 5
  2. float values[N_VALUES];
  3. float *vp;
  4. for( vp = &values[0]; vp < &values[N_VALUES]; )
  5. *vp++ = 0;

/********************************************************************/

33.

P.167


  1. /*
  2. ** Find the place in an array where a particular integer value
  3. ** is stored, and return a pointer to that location.
  4. */
  5. #include <stdio.h>
  6. int *
  7. find_int( int key, int array[], int array_len )
  8. {
  9. int i;
  10. /*
  11. ** For each location in the array ...
  12. */
  13. for( i = 0; i < array_len; i += 1 )
  14. /*
  15. ** Check the location for the desired value.
  16. */
  17. if( array[ i ] == key )
  18. return &array[ i ];
  19. return NULL;
  20. }

/********************************************************************/


34.

P. 198

The type of b[4] is integer,
The type of b is "constant pointer to int ".


"c = &a[0];"  ===  "c=a;"


/********************************************************************/


35.

p.199

int a[10];
int b[10];

b = a <-- illegal , b and a are constant pointer, so it cannot be changed.


/********************************************************************/


36.

P.201

A subscript = b[10]

- is a indirection expression


/********************************************************************/


37.

P.202

int array[5] = {1,2,3,4,5};

2[array]   is  legal  // ans = 3

- It same as *(array+2)


/********************************************************************/


*38.

p.202

用pointer玩array會快些

example:


 int   array[10], a;
            for( a = 0; a < 10; a += 1 )
                  array[a] = 0;
 

To evaluate this subscript, the compiler inserts instructions in the program to take the value of a and multiply it by the size of an integer (say, four). This multiplication takes both space and time.


-----------------------


  int   array[10], *ap;
            for( ap = array; ap < array + 10; ap++ )
                  *ap = 0;


The multiplication is still in here somewhere, even though there is no longer any subscript. Look closely now and see if you can find it.
The multiplication is now performed in the adjustment step of the for statement; the value one must be scaled to the size of an integer before it is added to the pointer. But there is a big difference here: the same two values (1 × 4) are multiplied each time through the loop. As a result, this multiplication is performed once at compile time—the program now contains an instruction to add four to the pointer. No multiplications are performed at run time.


/********************************************************************/


  39.

P.210

int a[5];
int *b;


the declarations the expression *a is perfectly legal, but the expression *b is not. *b will access some indeterminate location in memory or cause the program to terminate. On the other hand, the expression b++ will compile, but a++ will not because the value of a is a constant.


/********************************************************************/


*40.

p.219

int matrix [3] [10];

matrix  <- "pointer to an array of ten integers"

matrix <- it points to first row == matrix[0][0]

*(matrix+1) <- it points to second row == point to matrix[1][0]

*(matrix+1)+5 <- == point to matrix[1][5]


/********************************************************************/


41.

P.222

int (*p)[10] ;


- Subscripts have a higher precedence than indirection, but the parentheses surrounding the indirection force it to go first. So p is a pointer to something, but to what?

- p is a pointer to an array of ten integers.

--------------

int matrix[3][10];

p=matrix; // makes p point to first row of matrix

-------------


      int   *pi = &matrix[0][0];
      int   *pi = matrix[0];

both are legal


 -----------


If you intend to perform any arithmetic with the pointer, avoid this kind of declaration: 

      int   (*p)[] = matrix
 
p is still a pointer to an array of integers, but the array size is missing. Integers involved in pointer arithmetic with this variable will be scaled by the size of an empty array (that is, multiplied by zero), which is probably not what you had in mind. Some compilers catch this error and some donʹt.


/********************************************************************/


42.

P.223


int   vector[10];
            ...
            func1( vector );
The type of the argument vector is a pointer to an integer, so func1 can be prototyped in either of the following ways:
            void func1( int *vec );
            void func1( int vec[] );
 
 --------------


  int   matrix[3][10];
            ...
            func2( matrix );

 The type of the argument matrix is a pointer to an array of ten integers, so func2 can be prototyped in either of the following ways:

            void func2( int (*mat)[10] );
            void func2( int mat[][10] );


 **
The key here is that the compiler must know the sizes of the second and subsequent dimensions in order to evaluate subscripts, thus the prototype must declare these dimensions. The size of the first dimension isnʹt needed because it is not used in the calculation of subscripts.


------


Specifically, it is incorrect to prototype func2 like this: 
    void func2(int **mat);


This example declares mat to be a pointer to a pointer to an integer, which is not at all the same thing as a pointer to an array of ten integers. 



/********************************************************************/


 43.

P. 227


 int   *api[10];
 api is an array of pointers to integers


Where would you ever use an array of pointers? Here is one example:
      char        const *keyword[] = {
                  "do",
                  "for",
                  "if",
                  "register",
                  "return",
                  "switch",
                  "while"
      };

//test
    printf("%lu\n", sizeof(keyword));
    printf("%lu\n", sizeof(keyword[0]));
    printf("%lu\n", sizeof(keyword[2]));
    printf("%lu\n", sizeof(keyword[3]));// result
56
8
8
8
// comment
在mac的gcc測試下,
sizeof(char) = 1 ,  sizeof(char *) = 8.
sizeof(int)=4 , sizeof(int*) = 8.


Example:

include <string.h>
  
char        const *keyword[] = {
                  "do",
                  "for",
                  "if",
                  "register",
                  "return",
                  "switch",
                  "while"
      };
      #define     N_KEYWORD
                  ( sizeof( keyword ) / sizeof( keyword[0] ) )

 
 

int
lookup_keyword( char const * const desired_word,

    char const *keyword_table[], int const size )
{

}



char  const **kwp;     // kwp 要const, 因為準備指向const pointer array

/*
** For each word in the table ...
*/
for( kwp = keyword_table; kwp < keyword_table + size; kwp++ )

      /*
      ** If this word matches the one we're looking for,
      ** return its position in the table.
      */
      if( strcmp( desired_word, *kwp ) == 0 )

            return kwp - keyword_table;

/*
** Not found.
*/
return -1;


// comment
sizeof( keyword ) gives the number of bytes in the entire array, and sizeof( keyword[0] ) is the number of bytes in one element. 


We could also store the keywords in a matrix, like this:
      char        const keyword[][9] = {
                  "do",
                  "for",
                  "if",
                  "register",
                  "return",
                  "switch",
                  "while"
};



- Notice the difference in the amount of memory used. The matrix looks inefficient because every row must be long enough to store the longest keyword. But it does not need any pointers. On the other hand, the array of pointers takes space, but each of the string literals is only as long as it needs to be.
- but the matrix is more compact because there is no space needed for pointers. 

-----------------------------------------
P. 231
      char     const *keyword[] = {
                  "do",
                  "for",
                  "if",
                  "register",
                  "return",
                  "switch",
                  "while"
                 NULL 
}; 

 NULL identify the end of array , without using size
 like: 
  for( kwp = keyword;  *kwp!=NULL ; kwp++); 



/********************************************************************/