1. 程式人生 > >《 C和指標 》第6章問題與練習

《 C和指標 》第6章問題與練習

問題

1.如果一個值的型別無法簡單的通過觀察它的位模式來判斷,那麼機器是如何知道應該怎麼對這個值進行操縱的? 機器無法做出判斷。編譯器根據值的宣告型別建立適當的指令,機器只是盲目的執行指令。

2.C為什麼沒有一種方法來宣告字面值指標常量呢? 它們很少使用,因為你無法提前告訴編譯器放置變數的位置。

3.假定一個整數的值為244,為什麼機器不會把這個值解釋為一個記憶體地址呢? 該值是一個整數,因此編譯器不會生成間接引用它的指令。

4.在有些機器上,編譯器在記憶體位置零儲存0這個值,對NULL指標進行解引用操作將訪問這個位置,這種方法產生什麼後果? 這是危險的,首先,間接引用NULL指標的結果是因編譯器而異,因此程式不應該這樣做。 在這種訪問之後允許程式繼續是不幸的,因為該程式很可能無法正常執行。

5.表示式(a)和(b)的求值過程有沒有什麼區別?如果有的話,區別在哪裡?假定變數offset的值為3. Even if offset has the same value as the literal(字面值) in the next expression.it is more time consuming(消耗) to evaluate(計算) the first expression because the multiplication to scale(規模) offset to the size of an integer must be done at run time.This is because the variable might contain any value,and the compiler has no way of knowing ahead of time what the value might actually be.On the other hand,The literal three can be scaled to an integer by multiplying it at compiler time,and the result of that multiplication is simply added to p at run time,In other words,the second expression can be implemented by simply adding 12 to p(on a machine with four-byte integers);no runtime multiplication is needed. 即使offset跟後面一個表示式的字面值的值相等,但它比計算第一個表示式要消耗更多的時間,因為對整型大小的偏移量必須在執行時才知道,這是因為變數可能包括更多的值,而編譯器不能在變數賦值之前知道它的值,另一方面,在編譯的時候字面值3可以被縮放到一個整數中,結果在執行是新增到p,換句話說,第二個表示式可以簡單的用12加上p,在四位元組整形機器上,不需要執行時再相加.

6.下面的程式碼有沒有問題,如果有的話,問題在哪裡?

int array[ARRAY_SIZE];
int *pi;

for(pi = &array[0];pi < array[ARRAY_SIZE];)
    *++pi = 0;

1.對增值之後的指標進行間接訪問時,陣列的第一個元素沒有被初始化為0 2.指標在越過陣列的右邊界以後仍然進行間接訪問,它將把其他某個記憶體地址的內容清零。注意pi在陣列之後立即宣告,如果編譯器恰好把它放在緊跟陣列之後的記憶體位置,結果將是災難性的。當指標移到陣列後面的那個記憶體位置時,那個最後被清零的記憶體位置就是儲存指標的位置。這個指標(現在變為了0)因此仍然小於&array[ARRAY_SIZE],所以迴圈將會繼續執行。指標在它被間接訪問之前增值,所以下一個被破壞的值就是儲存於記憶體位置為4的變數(假定整數的長度為4個位元組)。如果硬體沒有捕捉到這個錯誤並終止程式,這個迴圈將快樂的繼續下去,指標在記憶體中歡樂的前行,破壞它所遇見的所有值。當它再一次到達這個陣列的位置時,就會重複上面這個過程,從而導致一個***微妙的無限迴圈***。

7.下面的表顯示了幾個記憶體位置的內容。每個位置由它的地址和儲存於該位置的變數名標識。所有數字以十進位制形式表示。使用這些值,用4中方法分別計算下面各表示式的值,首先,假定所有的變數都是整型,找到表示式的右值,再找到表示式的左值,給出它所指定的記憶體位置的地址。接著,假定所有的變數都是指向整型的指標,重複上述步驟,注意:在執行地址運算時,假定整型和指標的長度都是4個位元組。 這裡寫圖片描述

a. m 
b. v + 1 
c. j – 4 
d. a – d 
e. v – w
f. &c 
g. &e + 1 
h. &o – 4 
i. &( f + 2 ) 
j. *g
k. *k + 1
l. *( n + 1 ) 
m. *h – 4 
n. *( u – 4 )
o. *f – g 
p. *f - *g 
q. *s - *q 
r. *( r – t ) 
s. y > i 
t. y > *i
u. *y > *i 
v. **h 
w. c++ 
x. ++c 
y. *q++ 
z. (*q)++
aa. *++q
bb. ++*q 
cc. *++*q 
dd. ++*(*q)++

a. 表示式m 整型: --------右值 1008 --------左值地址 1016

整型指標: --------右值 1008 --------左值地址 1016 這裡寫圖片描述

b. 表示式v+1 整型: --------右值 1037 --------左值地址 非法

整型指標: --------右值 1040 (1036+1*4)

書上6.13指標運算方法 這裡寫圖片描述 這裡變數v作為整型和整型指標表示式v+1的左值地址結果即儲存於計算機記憶體的特定位置,我們都無法知道,沒有清晰定義儲存位置,所以都不是合法的左值 這裡寫圖片描述 --------左值地址 非法

c. 表示式j-4 整型: --------右值 996 --------左值地址 非法

整型指標: --------右值 984 (1000-4*4) 這裡寫圖片描述 --------左值地址 非法

d. 表示式a-d 整型: --------右值 12 --------左值地址 非法

整型指標: --------右值 3 <(1028-1016)/4> 這裡寫圖片描述 --------左值地址 非法

e. 表示式v-w 整型: --------右值 -24 --------左值地址 非法

整型指標: --------右值 -6 <(1036-1060)/4> 這裡寫圖片描述 --------左值地址 非法

f. 表示式&c 整型: --------右值 1056 --------左值地址 非法

整型指標: --------右值 1056 在這裡插入圖片描述 --------左值地址 非法

g. 表示式&e+1 整型: --------右值 1036 (1032+1 * 4) 在這裡插入圖片描述 --------左值地址 非法

整型指標: --------右值 1036(1032+1 * 4) 在這裡插入圖片描述 --------左值地址 非法

h. 表示式&o-4 (和g解析過程類似) 整型: --------右值 1080 --------左值地址 非法

整型指標: --------右值 1080 --------左值地址 非法

i. 表示式&(f+2) 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 非法 --------左值地址 非法

j. 表示式*g 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1000 在這裡插入圖片描述 --------左值地址 1064 在這裡插入圖片描述 由此可以看到,*操作符使機器指向記憶體中某個特定位置的地址,作為左值使用時,這個表示式指定需要進行修改的位置,作為右值使用時,則提取當前儲存於這個位置的值。

k. 表示式*k+1 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1045 在這裡插入圖片描述 --------左值地址 非法 表示式的最終結果的儲存位置沒有清晰定義,不是一個合法的左值

l. 表示式*(n+1) 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1012 --------左值地址 1060 在這裡插入圖片描述

m. 表示式*h-4 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1056 在這裡插入圖片描述 --------左值地址 1076

n. 表示式*(u-4) 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1056 --------左值地址 1076 在這裡插入圖片描述

o. 表示式*f-g 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 非法 --------左值地址 非法

p. 表示式*f-*g 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 52(1052-1000) 在這裡插入圖片描述 --------左值地址 非法

q. 表示式*s-*q 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 -80(1000-1080) 在這裡插入圖片描述 --------左值地址 非法

r. 表示式*(r-t) 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 非法 --------左值地址 非法 只有當兩個指標都指向同一個陣列中的元素時,才允許從一個指標減去另外一個指標,減法運算的結果是兩個指標在記憶體中的距離。

s. 表示式y>i 整型: --------右值 0 --------左值地址 非法

整型指標: --------右值 0 在這裡插入圖片描述 --------左值地址 非法

t. 表示式y>*i 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 非法 --------左值地址 非法

u. 表示式*y>*i 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1 在這裡插入圖片描述 --------左值地址 非法

v. 表示式**h 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1080 在這裡插入圖片描述 --------左值地址 1020 在這裡插入圖片描述

w. 表示式c++ 整型: --------右值 1076 字尾++操作符先返回變數c值的一份拷貝再增加c的值 --------左值地址 非法

整型指標: --------右值 1076 在這裡插入圖片描述 --------左值地址 非法

x. 表示式++c 整型: --------右值 1077 字首++操作符先增加c的值再返回變數c值增值後的結果,的一份拷貝 --------左值地址 非法

整型指標: --------右值 1080 在這裡插入圖片描述 --------左值地址 非法

y. 表示式*q++ 整型: --------右值 非法 --------左值地址 非法

整型指標: *q++表示式的執行步驟(字尾++操作符的優先順序>*操作符的優先順序): (1)++操作符產生q的一份拷貝 (2)++操作符增加q的值 (3)在q的拷貝上執行間接訪問操作 --------右值 1080 在這裡插入圖片描述 --------左值地址 1072 在這裡插入圖片描述

z. 表示式(*q)++ 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1080 在這裡插入圖片描述 --------左值地址 非法

aa. 表示式*++q 整型: --------右值 非法 --------左值地址 非法

整型指標: 間接訪問操作符作用於增值後的指標的拷貝上 --------右值 1056 在這裡插入圖片描述 --------左值地址 1076 在這裡插入圖片描述

bb. 表示式++*q 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1081 在這裡插入圖片描述 --------左值地址 非法

cc. 表示式*++*q 表示式++*q的基礎上進行間接訪問 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 非法 --------左值地址 非法 參考答案上右值1072 左值1084 沒看明白

dd. 表示式++*(*q)++ 表示式(*q)++的基礎上進行間接訪問然後是自增操作 整型: --------右值 非法 --------左值地址 非法

整型指標: --------右值 1021(++1020=1021) 在這裡插入圖片描述 --------左值地址 非法

程式設計練習

1.請編寫一個函式,它在一個字串中進行搜尋,查詢所有在一個給定字元集合中出現的字元。這個函式的原型應該如下: char *find_char(char const *source,char const *chars); 它的基本想法是查詢source字串中匹配chars字串中任何字元的第一個字元,函式然後返回一個指向source中第一個匹配所找到的位置的指標,如果任何一個引數NULL,或任何一個引數所指向的字串為空,函式也返回一個NULL指標。 舉個例子,假定source指向ABCDEF。如果chars指向XYZ、JURY或QQQQ,函式就返回一個NULL指標。如果chars指向XRCQEF,函式就返回一個指向source中C字元的指標。引數所指向的字串是不會被修改的。 碰巧,C函式庫中存在一個名叫strpbrk的函式,它的功能幾乎和這個你要編寫的函式一模一樣。但這個程式的目的是讓你自己練習操縱指標,所以: a 你不應該使用任何用於操縱字串的庫函式(如strcpy,strcmp,index等) b 函式中的任何地方都不應該使用下標

VC6.0環境

#include "stdafx.h"
#include <string.h>

char *find_char(char const *source,char const *chars);

//char *source_char = "ABCDEF";
//char *test_char   = "qqqq";
//char *test_char   = "XRCQEF";
//char *test_char   = NULL;
//char *source_char = NULL;
char *source_char = " ";
char *test_char   = "A";

char *find_char_result = NULL;

int main(int argc, char* argv[])
{	
			
	find_char_result = find_char(source_char,test_char);
	
	if(find_char_result != NULL)
		printf("find result %c\n",*find_char_result);
	else
		printf("find result is NULL\n");
	
	return 0;
}

char *find_char(char const *source,char const *chars)
{	
	char const *cp;
	/*
	** Check arguments for NULL
	*/
	if( source != NULL && chars != NULL )
	{
		/*
		** Look at ’source’ one character at a time.
		*/
		for( ; *source != '\0'; source++ )
		{
			/*
			** Look through ’chars’ one at a time for a
			** match with *source.
			*/
			for( cp = chars; *cp != '\0'; cp++ )
			{
				if( *source == *cp )
					return (char *)source;				
			}
				
		}
	}
	return NULL;
}

2.請編寫一個函式,刪除一個字串的一部分。函式的原型如下: int del_substr(char *str,char const *substr); 函式首先應該判斷substr是否出現在sub中。如果它並未出現,函式就返回0;如果出現,函式應該把str中位於該子串後面的所有字元複製到該子串的位置;從而刪除這個子串,然後函式返回1,如果substr在函式中出現多次,函式只刪除第一次出現的子串。函式的第二個引數不能被修改。 舉個例子,假定str指向ABCDEFG。如果substr指向FGH,CDF或XABC,函式返回0,str未作任何修改,但如果substr指向CDE,函式就把str修改為指向ABFG,方法是把FG和結尾的NUL位元組複製到C的位置,然後函式返回1.不論出現什麼情況,第二個引數都不能被修改。 a 你不應該使用任何用於操縱字串的庫函式 b 函式中的任何地方都不應該使用下標引用 一個值得注意的地方是,空字串是每一個字串的子串,在字串中刪除一個空字串不會產生任何變化。

VC6.0環境

#include "stdafx.h"

#define NUL '\0'
#define TRUE 1
#define FALSE 0

char *match( char *str, char const *want );
int del_substr(char *str,char const *substr);

//指標常量不能夠通過指標修改記憶體中的資料
//char *str = "ABCDEFG";
//char *substr = "ABC";

char str[] = "ABCDEFG";
char substr[] = "ABC";

int main(int argc, char* argv[])
{					
	if( TRUE == del_substr(str,substr) )
		printf("del_substr done!\n");
	else
		printf("del_substr fail!\n");
	
	return 0;
}

/*
** See if the substring beginning at ’str’ matches the string ’want’. If
** so, return a pointer to the first character in ’str’ after the match.
*/
char *match( char *str, char const *want )
{
/*
** Keep looking while there are more characters in ’want’. We fall out
** of the loop if we get a match.
*/
	while( *want != NUL )
		if( *str++ != *want++ )
			return NULL;
	return str;
}

/*
** If the string "substr" appears in "str", delete it.
*/
int del_substr( char *str, char const *substr )
{
	char *next = NULL;

	/*
	** Look through the string for the first occurrence of the substring.
	*/
	while( *str != NUL )
	{
		next = match( str, substr );
		if( next != NULL )
			break;
		str++;
	}
	/*
	** If we reached the end of the string, then the substring was not
	** found.
	*/
	if( *str == NUL )
		return FALSE;
	/*
	** Delete the substring by copying the bytes after it over the bytes of
	** the substring itself.
	*/
	while( *str++ = *next++ )
	;

	return TRUE;
}

3.編寫函式reverse_string,它的原型如下: void reverse_string(char *string); 函式把引數字串中的字元反向排列,請使用指標而不是陣列下標,不要使用任何C函式庫中用於操縱字串的函式。提示:不需要宣告一個區域性變數陣列來臨時儲存引數字串。

VC6.0環境

#include "stdafx.h"

char test_str[] = "abcd";//dcba

void reverse_string( char *string);

int main(int argc, char* argv[])
{					
	reverse_string(test_str);
	
	return 0;
}

void reverse_string( char *string)
{
	char *last_char = NULL;
	char temp = 0;
	
	if(string == NULL)
		return;
	
	/*把last_char設定為指向字串的最後一個字元 */
	for(last_char = string;*last_char != '\0';last_char++)
		;
	last_char--;
	
	/*交換string和last_char指向的字元,然後string和last_char相互靠近,在兩個指標相遇或者擦肩而過之前重複這個過程*/
	while(string < last_char)
	{
		temp = *string;
		*string++ = *last_char;
		*last_char-- = temp;
	}
}

#if 0
void reverse_string( char *string)
{
	char *start = string;
	char *end   = NULL;
	char temp   = 0;

	if(string == NULL)
		return;
	
	while( *string != '\0')
		string++;
	
	end = string;//end 指向'\0',字串的最後一個字元
	
	while( start != end ) //進行內容字元交換
	{
		end--;
		temp  = *start;
		*start = *end;
		*end   = temp;
		start++;
	}
	//所有字元交換完成
}
#endif