當前位置:首頁 » 編程語言 » c語言指針練習

c語言指針練習

發布時間: 2023-02-10 17:15:45

1. c語言指針練習,求解答,快!!!!麻煩幫找錯!!

for(j=10;j<=i+1;j--)
{
*(p+j)=*(p+j-1);
}
使用錯誤;
而且應該將語句*(p+i)=k放在其後。
正確而寫法:
#include <stdio.h>
#include <stdlib.h>

int main()
{
int a[11]={20,18,16,14,12,10,8,6,4,2};
int k,*p,i=0;
int j=0;
p=&a[0];
printf("input k\n");
scanf("%d",&k);
while (*(p+i)>=k&&i<10) i++;
printf("i=%d\n",i);

for(j=10;j>i;j--)
*(p+j) = *(p+j-1);
*(p+i)=k;
for(i=0;i<=10;i++)
printf("%4d",*(p+i));

return 0;
}

2. c語言指針練習題動態內存分配方式計算學生成績

參考
void getMemeory1(char *p)
{
p = (char *)malloc(100*sizeof(char));
}

char * getMemeory2()
{
char p[100] = "Hello world";
return p;
}

int main()
{
//test1
char *str1 = NULL;
getMemeory1(str1);
strcpy(str1, "Hello world");
printf("str1 = %s\n", str1);

//test2
char *str2 = NULL;
str2 = getMemeory2();
printf("str2 = %s\n", str2);
}

執行結果:test1程序報錯,test2列印亂碼或者「Hello world」(一般亂碼)

結果分析:
test1:
在函數中給指針分配空間,實際上是給指針的臨時變數分配空間,函數結束後,這個臨時變數也消亡,而str仍然為NULL,沒有為其分配空間,此時strcpy()是肯定會出錯的。

test2:
1、可能是亂碼,也有可能是正常輸出,因為GetMemory返回的是指向「棧內存」的指針,該指針的地址
不是NULL,但其原來的內容已經被清除,新內容不可知,程序員面試寶典里有專門講該部分知識的。
2、因為p的生命周期在GetMemory函數執行完了就被銷毀了,str 指向的是個野指針。

//相關分析資料摘錄
1、指針參數是如何傳遞內存的?

如果函數的參數是一個指針,不要指望用該指針去申請動態內存。以下Test函數的語句GetMemory(str, 200)並沒有使str獲得期望的內存,str依舊是NULL,為什麼?
1. void GetMemory(char *p, int num)
2. {
3. p = (char *)malloc(sizeof(char) * num);
4. }
5. void Test(void)
6. {
7. char *str = NULL;
8. GetMemory(str, 100); // str 仍然為 NULL
9. strcpy(str, "hello"); // 運行錯誤
10. }
復制代碼
示例1.1 試圖用指針參數申請動態內存

毛病出在函數GetMemory中。編譯器總是要為函數的每個參數製作臨時副本,指針參數p的副本是 _p,編譯器使 _p = p。如果函數體內的程序修改了_p的內容,就導致參數p的內容作相應的修改。這就是指針可以用作輸出參數的原因。在本例中,_p申請了新的內存,只是把 _p所指的內存地址改變了,但是p絲毫未變。所以函數GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory就會泄露一塊內存,因為沒有用free釋放內存。

如果非得要用指針參數去申請內存,那麼應該改用「指向指針的指針」,見示例1.2。
1. void GetMemory2(char **p, int num)
2. {
3. *p = (char *)malloc(sizeof(char) * num);
4. }
5. void Test2(void)
6. {
7. char *str = NULL;
8. GetMemory2(&str, 100); // 注意參數是 &str,而不是str
9. strcpy(str, "hello");
10. cout<< str << endl;
11. free(str);
12. }
復制代碼
示例1.2用指向指針的指針申請動態內存

由於「指向指針的指針」這個概念不容易理解,我們可以用函數返回值來傳遞動態內存。這種方法更加簡單,見示例1.3。
1. char *GetMemory3(int num)
2. {
3. char *p = (char *)malloc(sizeof(char) * num);
4. return p;
5. }
6. void Test3(void)
7. {
8. char *str = NULL;
9. str = GetMemory3(100);
10. strcpy(str, "hello");
11. cout<< str << endl;
12. free(str);
13. }
復制代碼
示例1.3 用函數返回值來傳遞動態內存

用函數返回值來傳遞動態內存這種方法雖然好用,但是常常有人把return語句用錯了。這里強調不要用return語句返回指向「棧內存」的指針,因為該內存在函數結束時自動消亡,見示例1.4。
1. char *GetString(void)
2. {
3. char p[] = "hello world";
4. return p; // 編譯器將提出警告
5. }
6. void Test4(void)
7. {
8. char *str = NULL;
9. str = GetString(); // str 的內容是垃圾
10. cout<< str << endl;
11. }
復制代碼
示例1.4 return語句返回指向「棧內存」的指針

用調試器逐步跟蹤Test4,發現執行str = GetString語句後str不再是NULL指針,但是str的內容不是「hello world」而是垃圾。
如果把示例1.4改寫成示例1.5,會怎麼樣?
1. char *GetString2(void)
2. {
3. char *p = "hello world";
4. return p;
5. }
6. void Test5(void)
7. {
8. char *str = NULL;
9. str = GetString2();
10. cout<< str << endl;
11. }
復制代碼
示例1.5 return語句返回常量字元串

函數Test5運行雖然不會出錯,但是函數GetString2的設計概念卻是錯誤的。因為GetString2內的「hello world」是常量字元串,位於靜態存儲區,它在程序生命期內恆定不變。無論什麼時候調用GetString2,它返回的始終是同一個「只讀」的內存塊。

2、杜絕「野指針」

「野指針」不是NULL指針,是指向「垃圾」內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是「野指針」是很危險的,if語句對它不起作用。 「野指針」的成因主要有兩種:

(1)指針變數沒有被初始化。任何指針變數剛被創建時不會自動成為NULL指針,它的預設值是隨機的,它會亂指一氣。所以,指針變數在創建的同時應當被初始化,要麼將指針設置為NULL,要麼讓它指向合法的內存。例如
1. char *p = NULL;
2. char *str = (char *) malloc(100);
復制代碼
(2)指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針。

(3)指針操作超越了變數的作用范圍。這種情況讓人防不勝防,示常式序如下:
1. class A
2. {
3. public:
4. void Func(void){ cout << 「Func of class A」 << endl; }
5. };
6. void Test(void)
7. {
8. A *p;
9. {
10. A a;
11. p = &a; // 注意 a 的生命期
12. }
13. p->Func(); // p是「野指針」
14. }
復制代碼
函數Test在執行語句p->Func()時,對象a已經消失,而p是指向a的,所以p就成了「野指針」。但奇怪的是我運行這個程序時居然沒有出錯,這可能與編譯器有關。

3. 學習C語言的指針部分有什麼好的方法嗎真的很難啊!!

C的精髓——指針

指針是一個特殊的變數,它裡面存儲的數值被解釋成為內存里的一個地址。 要搞清一個指針需要搞清指針的四方面的內容:指針的類型,指針所指向的 類型,指針的值或者叫指針所指向的內存區,還有指針本身所佔據的內存區。讓我們分別說明。
先聲明幾個指針放著做例子:
例一:
(1)int*ptr;
(2)char*ptr;
(3)int**ptr;
(4)int(*ptr)[3];
(5)int*(*ptr)[4];

指針的類型
從語法的角度看,你只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個指針的類型。這是指針本身所具有的類型。讓我們看看例一中各個指針的類型:
(1)int*ptr;//指針的類型是int*
(2)char*ptr;//指針的類型是char*
(3)int**ptr;//指針的類型是int**
(4)int(*ptr)[3];//指針的類型是int(*)[3]
(5)int*(*ptr)[4];//指針的類型是int*(*)[4]
怎麼樣?找出指針的類型的方法是不是很簡單?
指針所指向的類型
當你通過指針來訪問指針所指向的內存區時,指針所指向的類型決定了編譯器將把那片內存區里的內容當做什麼來看待。
從語法上看,你只須把指針聲明語句中的指針名字和名字左邊的指針聲明符*去掉,剩下的就是指針所指向的類型。例如:
(1)int*ptr;//指針所指向的類型是int
(2)char*ptr;//指針所指向的的類型是char
(3)int**ptr;//指針所指向的的類型是int*
(4)int(*ptr)[3];//指針所指向的的類型是int()[3]
(5)int*(*ptr)[4];//指針所指向的的類型是int*()[4]
在指針的算術運算中,指針所指向的類型有很大的作用。
指針的類型(即指針本身的類型)和指針所指向的類型是兩個概念。當你對C越來越熟悉時,你會發現,把與指針攪和在一起的"類型"這個概念分成"指針的類型"和"指針所指向的類型"兩個概念,是精通指針的關鍵點之一。我看了不少書,發現有些寫得差的書中,就把指針的這兩個概念攪在一起了,所以看起書來前後矛盾,越看越糊塗。
指針的值,或者叫指針所指向的內存區或地址
指針的值是指針本身存儲的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。在32位程序里,所有類型的指針的值都是一個32位整數,因為32位程序里內存地址全都是32位長。 指針所指向的內存區就是從指針的值所代表的那個內存地址開始,長度為si zeof(指針所指向的類型)的一片內存區。以後,我們說一個指針的值是XX,就相當於說該指針指向了以XX為首地址的一片內存區域;我們說一個指針指向了某塊內存區域,就相當於說該指針的值是這塊內存區域的首地址。
指針所指向的內存區和指針所指向的類型是兩個完全不同的概念。在例一中,指針所指向的類型已經有了,但由於指針還未初始化,所以它所指向的內存區是不存在的,或者說是無意義的。
以後,每遇到一個指針,都應該問問:這個指針的類型是什麼?指針指的類型是什麼?該指針指向了哪裡?
指針本身所佔據的內存區
指針本身佔了多大的內存?你只要用函數sizeof(指針的類型)測一下就知道了。在32位平台里,指針本身占據了4個位元組的長度。
指針本身占據的內存這個概念在判斷一個指針表達式是否是左值時很有用。
指針的算術運算
指針可以加上或減去一個整數。指針的這種運算的意義和通常的數值的加減運算的意義是不一樣的。例如:
例二:
1、chara[20];
2、int*ptr=a;
...
...
3、ptr++;
在上例中,指針ptr的類型是int*,它指向的類型是int,它被初始化為指向整形變數a。接下來的第3句中,指針ptr被加了1,編譯器是這樣處理的:它把指針ptr的值加上了sizeof(int),在32位程序中,是被加上了4。由於地址是用位元組做單位的,故ptr所指向的地址由原來的變數a的地址向高地址方向增加了4個位元組。
由於char類型的長度是一個位元組,所以,原來ptr是指向數組a的第0號單元開始的四個位元組,此時指向了數組a中從第4號單元開始的四個位元組。
我們可以用一個指針和一個循環來遍歷一個數組,看例子:
例三:
intarray[20];
int*ptr=array;
...
//此處略去為整型數組賦值的代碼。
...
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
}
這個例子將整型數組中各個單元的值加1。由於每次循環都將指針ptr加1,所以每次循環都能訪問數組的下一個單元。

再看例子:

例四:

1、chara[20];
2、int*ptr=a;
...
...
3、ptr+=5;
在這個例子中,ptr被加上了5,編譯器是這樣處理的:將指針ptr的值加上5乘sizeof(int),在32位程序中就是加上了5乘4=20。由於地址的單位是位元組,故現在的ptr所指向的地址比起加5後的ptr所指向的地址來說,向高地址方向移動了20個位元組。在這個例子中,沒加5前的ptr指向數組a的第0號單元開始的四個位元組,加5後,ptr已經指向了數組a的合法范圍之外了。雖然這種情況在應用上會出問題,但在語法上卻是可以的。這也體現出了指針的靈活性。

如果上例中,ptr是被減去5,那麼處理過程大同小異,只不過ptr的值是被減去5乘sizeof(int),新的ptr指向的地址將比原來的ptr所指向的地址向低地址方向移動了20個位元組。
總結一下,一個指針ptrold加上一個整數n後,結果是一個新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值增加了n乘sizeof(ptrold所指向的類型)個位元組。就是說,ptrnew所指向的內存區將比ptrold所指向的內存區向高地址方向移動了n乘sizeof(ptrold所指向的類型)個位元組。
一個指針ptrold減去一個整數n後,結果是一個新的指針ptrnew,ptrnew的類型和ptrold的類型相同,ptrnew所指向的類型和ptrold所指向的類型也相同。ptrnew的值將比ptrold的值減少了n乘sizeof(ptrold所指向的類型)個位元組,就是說,ptrnew所指向的內存區將比ptrold所指向的內存區向低地址方向移動了n乘sizeof(ptrold所指向的類型)個位元組。
運算符&和*
這里&是取地址運算符,*是...書上叫做"間接運算符"。
&a的運算結果是一個指針,指針的類型是a的類型加個*,指針所指向的類型是a的類型,指針所指向的地址嘛,那就是a的地址。
*p的運算結果就五花八門了。總之*p的結果是p所指向的東西,這個東西有這些特點:它的類型是p指向的類型,它所佔用的地址是p所指向的地址。
例五:
inta=12;
intb;
int*p;
int**ptr;
p=&a;
//&a的結果是一個指針,類型是int*,指向的類型是int,指向的地址是a的地址。
*p=24;
//*p的結果,在這里它的類型是int,它所佔用的地址是p所指向的地址,顯然,*p就是變數a。
ptr=&p;
//&p的結果是個指針,該指針的類型是p的類型加個*,在這里是int **。該指針所指向的類型是p的類型,這里是int*。該指針所指向的地址就是指針p自己的地址。
*ptr=&b;
//*ptr是個指針,&b的結果也是個指針,且這兩個指針的類型和所指向的類型是一樣的,所以用&b來給*ptr賦值就是毫無問題的了。
**ptr=34;
//*ptr的結果是ptr所指向的東西,在這里是一個指針,對這個指針再做一次*運算,結果就是一個int類型的變數。
指針表達式
一個表達式的最後結果如果是一個指針,那麼這個表達式就叫指針表式。
下面是一些指針表達式的例子:
例六:
inta,b;
intarray[10];
int*pa;
pa=&a;//&a是一個指針表達式。
int**ptr=&pa;//&pa也是一個指針表達式。
*ptr=&b;//*ptr和&b都是指針表達式。
pa=array;
pa++;//這也是指針表達式。
例七:
char*arr[20];
char**parr=arr;//如果把arr看作指針的話,arr也是指針表達式
char*str;
str=*parr;//*parr是指針表達式
str=*(parr+1);//*(parr+1)是指針表達式
str=*(parr+2);//*(parr+2)是指針表達式
由於指針表達式的結果是一個指針,所以指針表達式也具有指針所具有的四個要素:指針的類型,指針所指向的類型,指針指向的內存區,指針自身占據的內存。

好了,當一個指針表達式的結果指針已經明確地具有了指針自身占據的內存的話,這個指針表達式就是一個左值,否則就不是一個左值。
在例七中,&a不是一個左值,因為它還沒有占據明確的內存。*ptr是一個左值,因為*ptr這個指針已經占據了內存,其實*ptr就是指針pa,既然pa已經在內存中有了自己的位置,那麼*ptr當然也有了自己的位置。
數組和指針的關系
數組的數組名其實可以看作一個指針。看下例:
例八:
intarray[10]={0,1,2,3,4,5,6,7,8,9},value;
...
...
value=array[0];//也可寫成:value=*array;
value=array[3];//也可寫成:value=*(array+3);
value=array[4];//也可寫成:value=*(array+4);
上例中,一般而言數組名array代表數組本身,類型是int[10],但如果把array看做指針的話,它指向數組的第0個單元,類型是int*,所指向的類型是數組單元的類型即int。因此*array等於0就一點也不奇怪了。同理,array+3是一個指向數組第3個單元的指針,所以*(array+3)等於3。其它依此類推。

例九:
char*str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
chars[80];
strcpy(s,str[0]);//也可寫成strcpy(s,*str);
strcpy(s,str[1]);//也可寫成strcpy(s,*(str+1));
strcpy(s,str[2]);//也可寫成strcpy(s,*(str+2));
上例中,str是一個三單元的數組,該數組的每個單元都是一個指針,這些指針各指向一個字元串。把指針數組名str當作一個指針的話,它指向數組的第0號單元,它的類型是char**,它指向的類型是char*。
*str也是一個指針,它的類型是char*,它所指向的類型是char,它指向的地址是字元串"Hello,thisisasample!"的第一個字元的地址,即'H'的地址。 str+1也是一個指針,它指向數組的第1號單元,它的類型是char**,它指向的類型是char*。

*(str+1)也是一個指針,它的類型是char*,它所指向的類型是char,它指向 "Hi,goodmorning."的第一個字元'H',等等。

下面總結一下數組的數組名的問題。聲明了一個數組TYPEarray[n],則數組名稱array就有了兩重含義:第一,它代表整個數組,它的類型是TYPE[n];第二 ,它是一個指針,該指針的類型是TYPE*,該指針指向的類型是TYPE,也就是數組單元的類型,該指針指向的內存區就是數組第0號單元,該指針自己佔有單獨的內存區,注意它和數組第0號單元占據的內存區是不同的。該指針的值是不能修改的,即類似array++的表達式是錯誤的。
在不同的表達式中數組名array可以扮演不同的角色。
在表達式sizeof(array)中,數組名array代表數組本身,故這時sizeof函數測出的是整個數組的大小。
在表達式*array中,array扮演的是指針,因此這個表達式的結果就是數組第0號單元的值。sizeof(*array)測出的是數組單元的大小。
表達式array+n(其中n=0,1,2,....。)中,array扮演的是指針,故array+n的結果是一個指針,它的類型是TYPE*,它指向的類型是TYPE,它指向數組第n號單元。故sizeof(array+n)測出的是指針類型的大小。
例十
intarray[10];
int(*ptr)[10];
ptr=&array;:
上例中ptr是一個指針,它的類型是int(*)[10],他指向的類型是int[10] ,我們用整個數組的首地址來初始化它。在語句ptr=&array中,array代表數組本身。

本節中提到了函數sizeof(),那麼我來問一問,sizeof(指針名稱)測出的究竟是指針自身類型的大小呢還是指針所指向的類型的大小?答案是前者。例如:
int(*ptr)[10];
則在32位程序中,有:
sizeof(int(*)[10])==4
sizeof(int[10])==40
sizeof(ptr)==4
實際上,sizeof(對象)測出的都是對象自身的類型的大小,而不是別的什麼類型的大小。
指針和結構類型的關系
可以聲明一個指向結構類型對象的指針。
例十一:
structMyStruct
{
inta;
intb;
intc;
}
MyStructss={20,30,40};
//聲明了結構對象ss,並把ss的三個成員初始化為20,30和40。
MyStruct*ptr=&ss;
//聲明了一個指向結構對象ss的指針。它的類型是MyStruct*,它指向的類型是MyStruct。
int*pstr=(int*)&ss;
//聲明了一個指向結構對象ss的指針。但是它的類型和它指向的類型和ptr是不同的。
請問怎樣通過指針ptr來訪問ss的三個成員變數?
答案:
ptr->a;
ptr->b;
ptr->c;
又請問怎樣通過指針pstr來訪問ss的三個成員變數?
答案:
*pstr;//訪問了ss的成員a。
*(pstr+1);//訪問了ss的成員b。
*(pstr+2)//訪問了ss的成員c。
雖然我在我的MSVC++6.0上調式過上述代碼,但是要知道,這樣使用pstr來訪問結構成員是不正規的,為了說明為什麼不正規,讓我們看看怎樣通過指針來訪問數組的各個單元:
例十二:
intarray[3]={35,56,37};
int*pa=array;
通過指針pa訪問數組array的三個單元的方法是:
*pa;//訪問了第0號單元
*(pa+1);//訪問了第1號單元
*(pa+2);//訪問了第2號單元

從格式上看倒是與通過指針訪問結構成員的不正規方法的格式一樣。
所有的C/C++編譯器在排列數組的單元時,總是把各個數組單元存放在連續的存儲區里,單元和單元之間沒有空隙。但在存放結構對象的各個成員時,在某種編譯環境下,可能會需要字對齊或雙字對齊或者是別的什麼對齊,需要在相鄰兩個成員之間加若干個"填充位元組",這就導致各個成員之間可能會有若干個位元組的空隙。
所以,在例十二中,即使*pstr訪問到了結構對象ss的第一個成員變數a,也不能保證*(pstr+1)就一定能訪問到結構成員b。因為成員a和成員b之間可能會有若干填充位元組,說不定*(pstr+1)就正好訪問到了這些填充位元組呢。這也證明了指針的靈活性。要是你的目的就是想看看各個結構成員之間到底有沒有填充位元組,嘿,這倒是個不錯的方法。
過指針訪問結構成員的正確方法應該是象例十二中使用指針ptr的方法。
指針和函數的關系
可以把一個指針聲明成為一個指向函數的指針。intfun1(char*,int);
int(*pfun1)(char*,int);
pfun1=fun1;
....
....
inta=(*pfun1)("abcdefg",7);//通過函數指針調用函數。
可以把指針作為函數的形參。在函數調用語句中,可以用指針表達式來作為實參。
例十三:
intfun(char*);
inta;
charstr[]="abcdefghijklmn";
a=fun(str);
...
...
intfun(char*s)
{
intnum=0;
for(inti=0;i{
num+=*s;s++;
}
returnnum;
}
這個例子中的函數fun統計一個字元串中各個字元的ASCII碼值之和。前面說了,數組的名字也是一個指針。在函數調用中,當把str作為實參傳遞給形參s後,實際是把str的值傳遞給了s,s所指向的地址就和str所指向的地址一致,但是str和s各自佔用各自的存儲空間。在函數體內對s進行自加1運算,並不意味著同時對str進行了自加1運算。
指針類型轉換
當我們初始化一個指針或給一個指針賦值時,賦值號的左邊是一個指針,賦值號的右邊是一個指針表達式。在我們前面所舉的例子中,絕大多數情況下,指針的類型和指針表達式的類型是一樣的,指針所指向的類型和指針表達式所指向的類型是一樣的。
例十四:
1、floatf=12.3;
2、float*fptr=&f;
3、int*p;
在上面的例子中,假如我們想讓指針p指向實數f,應該怎麼搞?是用下面的語句嗎?

p=&f;

不對。因為指針p的類型是int*,它指向的類型是int。表達式&f的結果是一個指針,指針的類型是float*,它指向的類型是float。兩者不一致,直接賦值的方法是不行的。至少在我的MSVC++6.0上,對指針的賦值語句要求賦值號兩邊的類型一致,所指向的類型也一致,其它的編譯器上我沒試過,大家可以試試。為了實現我們的目的,需要進行"強制類型轉換":
p=(int*)&f;
如果有一個指針p,我們需要把它的類型和所指向的類型改為TYEP*TYPE, 那麼語法格式是:
(TYPE*)p;
這樣強制類型轉換的結果是一個新指針,該新指針的類型是TYPE*,它指向的類型是TYPE,它指向的地址就是原指針指向的地址。而原來的指針p的一切屬性都沒有被修改。
一個函數如果使用了指針作為形參,那麼在函數調用語句的實參和形參的結合過程中,也會發生指針類型的轉換。
例十五:
voidfun(char*);
inta=125,b;
fun((char*)&a);
...
...
voidfun(char*s)
{
charc;
c=*(s+3);*(s+3)=*(s+0);*(s+0)=c;
c=*(s+2);*(s+2)=*(s+1);*(s+1)=c;
}
}
注意這是一個32位程序,故int類型佔了四個位元組,char類型佔一個位元組。函數fun的作用是把一個整數的四個位元組的順序來個顛倒。注意到了嗎?在函數調用語句中,實參&a的結果是一個指針,它的類型是int*,它指向的類型是int。形參這個指針的類型是char*,它指向的類型是char。這樣,在實參和形參的結合過程中,我們必須進行一次從int*類型到char*類型的轉換。結合這個例子,我們可以這樣來想像編譯器進行轉換的過程:編譯器先構造一個臨時指針char*temp, 然後執行temp=(char*)&a,最後再把temp的值傳遞給s。所以最後的結果是:s的類型是char*,它指向的類型是char,它指向的地址就是a的首地址。

我們已經知道,指針的值就是指針指向的地址,在32位程序中,指針的值其實是一個32位整數。那可不可以把一個整數當作指針的值直接賦給指針呢?就象下面的語句:
unsignedinta;
TYPE*ptr;//TYPE是int,char或結構類型等等類型。
...
...
a=20345686;
ptr=20345686;//我們的目的是要使指針ptr指向地址20345686(十進制

ptr=a;//我們的目的是要使指針ptr指向地址20345686(十進制)
編譯一下吧。結果發現後面兩條語句全是錯的。那麼我們的目的就不能達到了嗎?不,還有辦法:
unsignedinta;
TYPE*ptr;//TYPE是int,char或結構類型等等類型。
...
...
a=某個數,這個數必須代表一個合法的地址;
ptr=(TYPE*)a;//呵呵,這就可以了。
嚴格說來這里的(TYPE*)和指針類型轉換中的(TYPE*)還不一樣。這里的(TYPE*)的意思是把無符號整數a的值當作一個地址來看待。上面強調了a的值必須代表一個合法的地址,否則的話,在你使用ptr的時候,就會出現非法操作錯誤。

想想能不能反過來,把指針指向的地址即指針的值當作一個整數取出來。完 全可以。下面的例子演示了把一個指針的值當作一個整數取出來,然後再把這個整數當作一個地址賦給一個指針:
例十六:
inta=123,b;
int*ptr=&a;
char*str;
b=(int)ptr;//把指針ptr的值當作一個整數取出來。
str=(char*)b;//把這個整數的值當作一個地址賦給指針str。
現在我們已經知道了,可以把指針的值當作一個整數取出來,也可以把一個整數值當作地址賦給一個指針。
指針的安全問題
看下面的例子:
例十七:
chars='a';
int*ptr;
ptr=(int*)&s;
*ptr=1298;
指針ptr是一個int*類型的指針,它指向的類型是int。它指向的地址就是s的首地址。在32位程序中,s佔一個位元組,int類型佔四個位元組。最後一條語句不但改變了s所佔的一個位元組,還把和s相臨的高地址方向的三個位元組也改變了。這三個位元組是干什麼的?只有編譯程序知道,而寫程序的人是不太可能知道的。也許這三個位元組里存儲了非常重要的數據,也許這三個位元組里正好是程序的一條代碼,而由於你對指針的馬虎應用,這三個位元組的值被改變了!這會造成崩潰性的錯誤。
讓我們再來看一例:
例十八:
1、chara;
2、int*ptr=&a;
...
...
3、ptr++;
4、*ptr=115;
該例子完全可以通過編譯,並能執行。但是看到沒有?第3句對指針ptr進行自加1運算後,ptr指向了和整形變數a相鄰的高地址方向的一塊存儲區。這塊存儲區里是什麼?我們不知道。有可能它是一個非常重要的數據,甚至可能是一條代碼。而第4句竟然往這片存儲區里寫入一個數據!這是嚴重的錯誤。所以在使用指針時,程序員心裡必須非常清楚:我的指針究竟指向了哪裡。在用指針訪問數組的時候,也要注意不要超出數組的低端和高端界限,否則也會造成類似的錯誤。
在指針的強制類型轉換:ptr1=(TYPE*)ptr2中,如果sizeof(ptr2的類型)大於sizeof(ptr1的類型),那麼在使用指針ptr1來訪問ptr2所指向的存儲區時是安全的。如果sizeof(ptr2的類型)小於sizeof(ptr1的類型),那麼在使用指針ptr1來訪問ptr2所指向的存儲區時是不安全的。至於為什麼,讀者結合例十七來想一想,應該會明白的。

摘錄的別人的:

C語言所有復雜的指針聲明,都是由各種聲明嵌套構成的。如何解讀復雜指針聲明呢?右左法則是一個既著名又常用的方法。不過,右左法則其實並不是C標准裡面的內容,它是從C標準的聲明規定中歸納出來的方法。C標準的聲明規則,是用來解決如何創建聲明的,而右左法則是用來解決如何辯識一個聲明的,兩者可以說是相反的。右左法則的英文原文是這樣說的:

The right-left rule: Start reading the declaration from the innermost parentheses, go right, and then go left. When you encounter parentheses, the direction should be reversed. Once everything in the parentheses has been parsed, jump out of it. Continue till the whole declaration has been parsed.

這段英文的翻譯如下:

右左法則:首先從最裡面的圓括弧看起,然後往右看,再往左看。每當遇到圓括弧時,就應該掉轉閱讀方向。一旦解析完圓括弧裡面所有的東西,就跳出圓括弧。重復這個過程直到整個聲明解析完畢。

筆者要對這個法則進行一個小小的修正,應該是從未定義的標識符開始閱讀,而不是從括弧讀起,之所以是未定義的標識符,是因為一個聲明裡面可能有多個標識符,但未定義的標識符只會有一個。

現在通過一些例子來討論右左法則的應用,先從最簡單的開始,逐步加深:

int (*func)(int *p);

首先找到那個未定義的標識符,就是func,它的外面有一對圓括弧,而且左邊是一個*號,這說明func是一個指針,然後跳出這個圓括弧,先看右邊,也是一個圓括弧,這說明(*func)是一個函數,而func是一個指向這類函數的指針,就是一個函數指針,這類函數具有int*類型的形參,返回值類型是int。

int (*func)(int *p, int (*f)(int*));

func被一對括弧包含,且左邊有一個*號,說明func是一個指針,跳出括弧,右邊也有個括弧,那麼func是一個指向函數的指針,這類函數具有int *和int (*)(int*)這樣的形參,返回值為int類型。再來看一看func的形參int (*f)(int*),類似前面的解釋,f也是一個函數指針,指向的函數具有int*類型的形參,返回值為int。

int (*func[5])(int *p);

func右邊是一個[]運算符,說明func是一個具有5個元素的數組,func的左邊有一個*,說明func的元素是指針,要注意這里的*不是修飾func的,而是修飾func[5]的,原因是[]運算符優先順序比*高,func先跟[]結合,因此*修飾的是func[5]。跳出這個括弧,看右邊,也是一對圓括弧,說明func數組的元素是函數類型的指針,它所指向的函數具有int*類型的形參,返回值類型為int。

int (*(*func)[5])(int *p);

func被一個圓括弧包含,左邊又有一個*,那麼func是一個指針,跳出括弧,右邊是一個[]運算符號,說明func是一個指向數組的指針,現在往左看,左邊有一個*號,說明這個數組的元素是指針,再跳出括弧,右邊又有一個括弧,說明這個數組的元素是指向函數的指針。總結一下,就是:func是一個指向數組的指針,這個數組的元素是函數指針,這些指針指向具有int*形參,返回值為int類型的函數。

int (*(*func)(int *p))[5];

func是一個函數指針,這類函數具有int*類型的形參,返回值是指向數組的指針,所指向的數組的元素是具有5個int元素的數組。

要注意有些復雜指針聲明是非法的,例如:

int func(void) [5];

func是一個返回值為具有5個int元素的數組的函數。但C語言的函數返回值不能為數組,這是因為如果允許函數返回值為數組,那麼接收這個數組的內容的東西,也必須是一個數組,但C語言的數組名是一個右值,它不能作為左值來接收另一個數組,因此函數返回值不能為數組。

int func[5](void);

func是一個具有5個元素的數組,這個數組的元素都是函數。這也是非法的,因為數組的元素除了類型必須一樣外,每個元素所佔用的內存空間也必須相同,顯然函數是無法達到這個要求的,即使函數的類型一樣,但函數所佔用的空間通常是不相同的。

作為練習,下面列幾個復雜指針聲明給讀者自己來解析,答案放在第十章里。

int (*(*func)[5][6])[7][8];

int (*(*(*func)(int *))[5])(int *);

int (*(*func[7][8][9])(int*))[5];

實際當中,需要聲明一個復雜指針時,如果把整個聲明寫成上面所示的形式,對程序可讀性是一大損害。應該用typedef來對聲明逐層分解,增強可讀性,例如對於聲明:

int (*(*func)(int *p))[5];

可以這樣分解:

typedef int (*PARA)[5];
typedef PARA (*func)(int *);

這樣就容易看得多了

4. 一道簡單C語言指針問題編程題!麻煩高手幫我改的簡單容易理解,英文單詞別很多還難!麻煩了

因為某種原因,本人共考過2次筆試,3次上機。其中筆試在70左右,3次上機均為滿分,
看著大批的同學在為三級發愁不知道如何准備和應考。我想我應該把經驗和學習的方法寫下
來,這些方法保證你能過三級。但是不保證你能獲得高分並學到知識,所以請各位計算機系
與愛好電腦的高手或自認為是高手的同志們就此打住。OK!

以網路技術為例(其他三門的方法相似),我把方法總結為:細看書,做筆記,多復習。
第一階段:看網路技術教材,詳細地做筆記(20天).
第一邊看書你要看得特別仔細,把所有你認為重要的地方全抄到筆記本上,包括重要的
表格和插圖。第一次看不懂的地方可以跳過去等過兩天再回頭看一遍。每天看新的知識之前
都將前兩天看的再復習一遍,強化記憶。
我把全書的重要程度劃分了一下:第一,七,八章 了解(看看做做筆記就行了);
第二,四章 理解(看懂主要的知識點);第三,五,六章 掌握(看懂所有的知識點);
其中又以以下章節為重點:2.2 進程 2.3 存儲管理 2.4 文件管理 2.5 設備管理
(我當時這一節的題都掛了) 3.5 雙絞線 VS 同軸電纜 3.6 OSI VS TCP/IP 4.3 乙太網
4.5 組網技術 4.8 網路互連 5.3 IP協議 6.4 加密技術 其中有些內容必須理解並掌握,
要是不理解,背也要背下來。如:OSI協議和TCP/IP協議的各層的名字,各種傳輸介質和各種
乙太網的參數等。
另外所有教材上沒有的內容不用看,因為考試出的題沒有一道是書本之外的。
===> 要點一:堅持就是勝利,每天堅持看2個小時,至少能看15頁。
===> 要點二:該抄就抄,好記性不如壞筆頭。
===> 要點三:注意課後作業題,那可能就是原題。
這一階段完成後,應該能夠理解大部分的知識點。還要能夠很快的在書中找到某個知識
點的位置。
第二階段:復習重點章節(5天)
這一次復習的重點在那些需要理解和掌握的內容上,如果你看懂了,合上書想一遍,看
不懂就多看兩遍,有些重點一定要記住。而且要把筆記本大聲地讀上兩遍,達到更加熟練。
===> 要點一:看懂不一定會記住,一定要在腦海中形成印象,合上書能寫下來。
===> 要點二:名詞術語要注意。
===> 要點三:縮略語。凡是在書上出現過兩遍的,一定要記下中英文解釋。
例如:CDMA/CD,MFLOPS等。
第三階段:復習不重要的章節和作題(5天)
有人也許要問,既然不重要還復習它干什麼。錯!出題人不能用重點湊成一百分要適當的
加點其他內容。於是只有在這些不重要的章節裡面出了,這些題大概佔20分左右。得分的方法
是將所有不太重要的章節再看一遍,越是你認為不可能出的,越要把它抄下來,記一記。而且
要特別注意數字,如ATM的傳輸速率,單模光纖所使用的光的波長等。(上次最嘔吐的的一道
題是,信號從發送站經過衛星到接收站的傳輸延遲的典型值是多少?我按照同步地球衛星到地
球的距離,光的傳輸速度,算到百毫秒的量級,但是算不出具體的值,於是猜了一個250ms。
答案居然是540ms。暈倒~!)
還有就是模擬題的問題,我認為現在市面上的模擬題都太差了,考的全沒有,不考的全有
了,說實話還不如我出的呢。所以題不用多做,只要把書看好就行,最後幾天可以做幾套,熟
悉一下題型,掌握一下時間就夠了,多做全是浪費。
以上方法我一個同學上次照著我說的做了,結果筆試考分比我還高。所以不管你聰明不聰
明,只要你照我說的做了,一定能通過。
一句話:要是不過你拿我是問。

上機部分現在只有一到c語言編程題,非常簡單。但我認為最重要的是能夠編譯程序和調試
程序,也就是說,一道題你能在紙上寫下來還不夠,一定要能夠在機器上運行出結果才行,因為
編譯系統及機器的不同,有的對的程序也可能產生大量的錯誤。只要你能把錯誤找出來就差不多
能過了。
因此我把方法總結為:多思考,多上機,捉蟲子。(以譚浩強的《c語言設計》第二版為例)
第一階段:看書,做筆記。(15天)
有人說那本書那麼厚,15天能看完嗎?錯!誰說讓你們看完了,你要是只是想過三級。只用
看前140頁就足夠了。也就是,第一章,了解,第二,三章理解,第五,六,七章掌握,第八,
第十章隨便看一下就行了。因為c上機只要求你填一個函數,所以函數那一章不用看太仔細,只
要知道函數是干什麼用的,它們之間是如何調用的就行了。至於指針,所有的考題全都能用數組
做出來,用指針只不過是簡單一點,容易出錯一點而已,我是強烈建議不用指針。第九章和第十
一章以後一概不用看。
===> 要點一:勤記,把所有重要的東西都記在筆記本上。如轉義字元的定義等。
===> 要點二:每天上機調試兩個程序(可以是例題或作業題)
===> 要點三:堅持,再堅持。
第二階段:做課後作業題並上機調試。(10天)
現在書你已看完,練習題也可能做出了一些。現在就是要把前幾章特別是五六七三章的課後
題一定要自己思考一下,做出來後再上機調試,是在做不出來,就看看答案,看懂後,再在機器
上調試一下也行。
重要例題:例 4.10 4.12 5.4 5.5 5.6 5.7 5.8 6.6 6.8 6.10 7.2 7.2 7.8 7.9
重要習題:4.5 4.9 5.5 5.7 5.8 6.1~6.10 7.1~7.14
===> 要點一:做題時使用N-S流程圖。
===> 要點二:每道題不管多麼簡單,一定要調試直到正確為止。如習題2.4(2)我有幾個同
學都是能編出來,但是怎麼都運行不正確。
===> 要點三:每天上機作五道題以上,最好在TC2.0環境下。
第三階段:繼續調試重要程序,看一本書(5天)
繼續調試程序直到熟練,然後看一本書,書名我先不說,可能許多同學都知道。但我不贊成
看這本書,而且我勸你到最後幾天再看,挑出一部分典型的題看一下,自己做做,看是否正確,
熟悉一下題型,不要完全依賴那本書。那樣可能因為一點小錯誤你找不出來就掛了。我們班的女
生們第一次因為這個原因掛的可不是少數。
還是那句話:要是你上機不過找我是問。

········回顧過去的大學生活,計算機等級考試最令人難忘。筆者斗膽在這里賣弄一下過去兩年(准確的說是一年半)里通過全國計算機等級考試二到四級的經歷,以自己的感受泡製一份全國計算機等級考試全攻略,希望對後來的朋友們尤其是剛剛邁入大學校門的師弟師妹們有所幫助。

考證路上,有兩點需要特別注意。一是一次只能報考一個等級(包括筆試和上機)的考試。一個級別中有不同類別,考生只能選擇其中一類。另外,考試單科合格者,成績只保留到下一個考試周期。所以不想找麻煩的話就要認真准備,爭取一次OK,否則會對信心造成打擊。

一級考試

對於一級考試,其實沒什麼好說的,大部分人不必報考這一級別,既浪費金錢又浪費生命。因而筆者強烈建議不要考這一級別。如果你不得不參加這一級別考試的話,筆者在表示同情之餘建議你在熟悉常用軟體之餘研究一下WPS,在以後的全國計算機等級考試(一級)中 ,WPS將成為一門單獨的考試科目。

二級考試

二級考試是大部分人都必須參加的。二級考試的內容包括基礎知識和程序設計兩大部分。 基礎知識主要包括電腦基本知識,DOS基本知識和基本操作,WINDOWS的基本概念與操作。關於這部分知識有點水準的輔導書就會有詳細的介紹,只要把書好好看看就沒什麼問題。DOS中的文件、目錄、路徑的概念及使用是必考部分,要留意。(相關閱讀:如何應付新大綱二級考試的公共基礎知識)

二級考試的重中之重是程序部分。要對語言的基礎知識熟練掌握,尤其要留意基礎概念和基本方法(選擇法、冒泡法等),要能看懂書上給的程序。一般來說,如果能獨立做出大部分課後題就一切OK了。對語言的工夫應該下在平時,多想,多練,多上機。自己考慮作一些小程序會有很大幫助的(別人崇敬的目光就會讓你覺得很有價值)。二級DOS類考試的上機考試包括三個部分的內容,即DOS操作題,程序修改題和程序編制題。如果你的C語言有一定水準的話,較難的部分就是程序修改題了,畢竟了解陌生人的思路是一件比較難的事。因此平時要多讀一些程序,自己編程時注意要盡量符合規范,否則很容易吃虧。當然多上機練習是取得好成績的不二法門。關於輔導書大連理工出版社和南開出版社的都相當不錯。

三級考試

三級是一個比較麻煩的等級。它共有四個類別,可以大致分兩種。 三級PC上機是考匯編程序,其他三個類別考C程序。如果不是對自己特別有信心的話一般不要報考PC技術。畢竟學習匯編語言也是不小的負擔 ,而且對報考四級沒什麼幫助(四級不考匯編!)。對於其他三種主要看自己的興趣了。相對來說資料庫要難一點(對數學系的除外)。三級的四個類別各有側重,比較麻煩的是對於四級來說都太片面了,所以幫助不大。因此基礎比較好的,直考四級也是可以考慮的。(相關閱讀:通過計算機等級考試三級網路的經驗)

筆試部分是三級的難點,所要記憶的內容比二級增加很多。要注意對基本理論和基礎概念的掌握,有利於知識網路的形成,減少記憶負擔。

由於三級剛剛改革不久,各個出版社出版的輔導書都很難貼近考試。這種情況下多看書就成了最好的選擇。從近兩次考試的情況看,出題也很接近課本(尤其是網路技術,有部分教材原話)。三級上機考試難度比二級提高並不大,較復雜的部分如文件讀寫,函數調用等通常題目已給出,因而所考的依然是比較基本和典型的演算法

四級考試

四級是最後一級,也是知識最復雜的一級。報考四級是需要一點信心和勇氣的,你報考四級後身邊幾乎不可避免的出現一片質疑聲(當我報四級時老師竟然告訴我本校從來沒人通過),這時候一定要相信自己。(相關閱讀:幫你全面了解全國計算級等級考試-四級)

四級通過的人數的確是非常少(相對三級),但要看到四級報考人數也是最少的。四級知識量是巨大的(看看那厚厚的教材就知道了),難點主要是離散數學和計算機英語。對於離散數學,如果有機會旁聽一下計算機專業的核心課程《離散數學》會很有幫助的。要記住一定要作一定數量的習題!僅僅通過識記是無法提高水平的。

計算機英語沒有想像中的困難,常用的單詞只有幾百個,只要把近5、6年的試題中英語題過一遍就差不多了。手頭最好有一本專業詞典,大部分單詞在文曲星上查不到 。相對來說英語題比較簡單,只要單詞認識就OK了。

筆試中比較新鮮的是論述題。建議買一本歷年試題及參考答案(每年國家考試中心都出版),仔細研究一下,掌握答題方法。比較幸運的是論述題通常有四道,可以選擇自己最有把握的。由於四級考試已經舉行多年,各出版社出的輔導書都比較扣題。筆者強烈推薦大連理工出版社出版的《全國計算機等級考試題典(四級)》。這本書貼近考試,解答詳盡,對順利過級會有很大幫助!一定要做歷年試題(5年以內),確實是有一定復現率的!

四級上機考試和三級差不多,只有一道編程題,注意別緊張,一定要存檔,否則會變為0分。當時跟我一個考場的同學就有人因為慌亂忘記存檔,造成下午的考試沒有通過。(相關閱讀:全國計算機四級考試經驗談)

結束語:最後,不管面對任何考試都需要對自己要信心,相信即使只有一個人通過,那個人也一定是自己!這時你就成功一半了。預祝每一位考生能夠在9月的考試中取得理想的成績!

計算機考試備忘錄

首先是報考時間問題。雖說大學有四年,但是如果你要考研究生的話(大部分人都會考的),從大三下學期就該開始准備了,所以留給等級考試的時間其實只有兩年半而已(四次機會)。因此,如果你是大一的新生,應該考慮報考明年四月的考試。因為全國計算機等級考試是面對非專業人士的,所以難度並不太高(當然也不很容易,否則聽我嘮叨什麼)。如果你的學校沒有一級必考之類的變態規定話,就應該堅決跳過一級從二級開考(時間寶貴,不應該為沒意義的事浪費半年時光)。平心而論,二級知識非常基礎,只要認真准備(你有整整一個寒假的時間准備)過關是很容易的(即使你的計算機基礎很差)。

雖然三級證書一樣可以拿到學位證,並且越級考試也是被許可的,但筆者不提倡直接報考三級(即使你的計算機基礎很好)。直接過三級雖然是一件很酷的事,但這要擔相當的風險,一旦失誤(這是很可能的事,畢竟三級是有一定難度的),你就不得不與剛通過二級的同學一起考試。除非你的神經有下水管道那麼粗,不然會有很大壓力,而且對信心的打擊不容忽視。如果你對自己的計算機水平很有自信的話,二級證書到手後跳過三級直考四級倒是可以考慮的,那樣風險和效率比較均衡。

另一個比較重要的問題是計算機語言的選擇問題。筆者強烈推薦學習C語言。C語言不但本身邏輯清楚,實用性強而且擴展性很強,方便以後學習C++,C#等語言。如果你有獅子一樣的雄心,更應該選擇C語言,要注意到四級和高級程序員考試都要求C語言!
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@計算機三級考試馬上就要到了,然而很多朋友對計算機上機考試卻仍舊十分茫然,

參加這次考試的人有很大一部分是上次機試沒有通過的朋友,甚至還有一些考了兩次機試都沒有通過的……

這其中不乏一些C語言學的很不錯的高手,大部分人到現在都不明白為什麼自己親手編的程序卻是0分……

究竟是什麼原因呢?不就是那100道題目嗎?翻來覆去的看也沒有發現自己的程序有什麼錯誤啊,看資料,用模擬盤模擬也是滿分啊,可為什麼正式考試就是0分呢?

於是很多人甚至把通過上機考試當成一件*運氣的事了,甚至有的朋友對上機考試感到十分恐懼
然而這其中真正的原因……
大家不妨和我一起看一下考試中心命題組的正確答案~
順便提醒一下大家在考試的時候要注意的問題,希望對大家有所幫助。

第一條:審題要仔細!正式考試的時候會遇到很多面熟的題目,這些題目有的似乎和
平常資料中看到的一樣,然而,千萬不要見到面熟的題目而沾沾自喜,因為有很多的
「陷阱」會使你審題的時候就出錯~!
我們看一道所有人都做過的的 「數字題目」 。
考試中心命題組第23題:
已知在文件in.dat中存有N個實數,(N<200),函數readdat()是讀取這N個實數並存入數組xx中。
請編制函數CalValue(),其功能要求:
1、求出這N個實數的整數部分平均值aver;
2、分別求出這N個實數中其整數部分值小於平均值ever的數的整數部分值之和sumint與小數部分之和sumdec
最後調用函數WriteDat()把所求的結果輸出到文件OUT8.dat中。
這一題看起來很眼熟,然而你仔細看~!
1、aver是整數部分的平均值~!不是普通的哦!
2、整數部分值小於平均值ever的數的整數部分值之和sumint與小數部分之和sumdec!!
呼~,原來這么復雜~!稍不留心就錯了~,結果就想當然的……0分
考試中心命題組第4題
函數ReadDat()實現從文件ENG.IN中讀取一篇英文文章存入到字元
串數組xx中;請編制函數ComWord()分別計算出10個不區分大小寫
的英文單詞的首字母(b,c,f,s,n,r,u,e,o,p)的頻數並依次存入
整型數組yy[0]至yy[9]中,最後調用函數WriteDat( )把結果yy
輸出到文件PS1.OUT中。
這題也很眼熟,然而你仔細看~!
英文單詞的首字母,呵呵,不是平常的字母的個數,是作為單詞的首字母的個數啊,暈~
上面兩道題目並沒有在其他資料上見過,考試的時候也不一定會出,
這里僅僅是給大家提個醒兒,正式考試一定要仔細審題,嚴格按照題目的要求去做,
不要受以前做過的題目的影響。

第二條:一定要考慮除零、溢出、類型轉換、精度等細節問題~!!
雖然我們在平時學C語言的時候老師都提醒過,可是真正到了編程序的時候,
卻沒有幾個人會考慮。這些細節問題似乎並不直接影響題目的輸出結果,
然而,因為溢出和類型轉換錯誤而造成考試得不到滿分的可是大有人在啊!
我們看下面一道最害人的題目~!!,很多朋友考了這道題目都沒有過。
下面來看看你為什麼是0分~!
考試中心命題組64題:
請編制函數encryptChar(),按給定的替代關系對數組xx中的所有字元進行替代,
仍存入數組xx的對應的位置上,最後調用函數WriteDat()把結果xx輸出到文件PS8.DAT中。
替代關系:f(p)=p*11 mod 256 (p是數組中某一個字元的ASCII值,f(p)是計算後新字元的ASCII值),
如果原字元小於等於32或大於130,則該字元不變,否則將f(p)所對應的字元進行替代。
這道題目所有的人都做過,但幾乎就沒有人做對,包括南開無憂在內~!
原因就在於這一句!
無憂:int f; f=(xx[i][k]*11)%256;
南開:char *pf; *pf=*pf*11%256;
表面看來,好象兩個都對,
請你在TC下運行一下下面的代碼,看一下結果你就知道了是怎麼回事了……:
main()
{
char nk='E',*nkp,wuyou='E';
int wy;
nkp=&nk;
*nkp=*nkp*11%256;
wy=wuyou*11%256;
printf("%c %c %c %c\n",nk,*nkp,wuyou,wy);
printf("%d %d %d %d\n",nk,*nkp,wuyou,wy);
}
看到結果了么?是不是很吃驚啊?
≈ ≈ E ≈
-9 -9 69 247
大家看到了,南開的結果是負數,連原來的變數nk都變成了負數,這顯然錯了。
無憂的聰明一點,知道int比char大,定義了個int,對了。
南開錯誤原因就是在TC2.0中char型變數只有一個位元組,根本容納不下'E'*11,
所以產生了溢出,上次考這道題目沒過的同學是不是有所啟發呢?
這里給出考試中心命題組的答案:
void encryptChar(){
int i,k,ch;
for(i=0;i<maxline;i++)
for(k=0;k<strlen(xx[i]);k++){
ch=xx[i][k];
ch=(ch*11)%256;
if(!(ch<=32||ch>=130))xx[i][k]=ch;
}
}
似乎一個例子不能說明什麼,那就把上面的數字題目的答案給大家看看:
考試中心命題組23題答案:
int i;
long val;
float dec;
for(i=0;i<N;i++){
val=(long)xx[i];
aver+=xx[i]-val;
}
aver/=(double)N;
for(i=0;i<N;i++){
val=(long)xx[i];
dec=xx[i]-val;
if(dec>aver){
sumint+=val;
sumdec+=(xx[i]-val);
}
}
看到了吧,首先,val的定義,不是我們習慣的int,而是能容納更大數的 long ,
這樣在求和的時候就不會溢出,無憂和南開的答案沒有吧~
再看,算aver平均值的細節,不是我們平常習慣的aver/=N,更不是無憂的1.0* sum/N,
而是aver/=(double)N,所以大家在考試的時候要時刻注意你的變數類型,特別是在一些求平均值的題目中,千萬要注意,用sum/n的時候,sum的類型一定不要用整形。

第三條:不要使用快捷鍵!!很多人在使用TC的時候習慣於用F9,CTRL+F9或者其他的熱鍵,
首先F9隻是編譯了,並沒有運行程序,考試一定要運行一下你的程序才會有輸出文件,
否則一定是0分。
熱鍵在考試的時候是很危險的,因為UCDOS的熱鍵和TC的沖突,CTRL+F9變成了全形半形轉換,
如果不小心按了,會導致你的程序有中文字元,編譯的時候出錯,而且很難查找排除錯誤~!
所以建議大家在考試的時候使用ALT+紅色字母 來調出菜單進行編譯調試等步驟,建議步驟為:
寫完代碼後用ALT+F,S存檔,然後ALT+C,M編譯,然後ALT+R,R運行,這樣就萬無一失了。

第四條:不要載入任何輸入法
上機考試用的DOS環境內存十分有限,如果你載入了任何一個輸入法,都有可能造成編譯程序的時候出現out of memory 內存不足的情況,到那時就麻煩了。
程序只要寫的對就可以,不要畫蛇添足的加註釋,沒有人看的~!
第五條:不要提前交卷!考場一般使用NOVELL網路,有的時候存在網路狀況不好而連接失敗等問題,
這時,如果你已經提前交卷的話就不太好了。另外也可以防止別人有意無意的改動你的程序。
第六條:考試時候的一些技巧。
1、進入TC以後如果想查看題干要求,不用退出TC,直接用F12就可以顯示出來。
2、用F12顯示題目之後可以通過CTRL+BACKSPACE把題目定在屏幕上。
3、編譯運行之後,可以通過type out.dat或edit out.dat或tc out.dat查看輸出文件。
(千萬不要改動!不然影響考試結果。如果不小心改動,再重新編譯運行一次你的程序就可以了)
4、如果考試的時候不小心改動了代碼的主函數或其他部分,可以進入考生目錄下的warn隱藏目錄,
把里邊的備份文件復制到考生目錄。步驟:
在考生目錄下輸入cd warn
輸入 *.c ..輸入cd ..
就可以了
5、如果調試的時候不小心死循環了,可以通過CTRL+C或CTRL+BREAK來終止,如果連按多次都沒反應,
不要慌,立即找監考老師說明情況,可以重新啟動機器繼續答原題,你的程序還在,時間也是繼續剛才的。
所以這也提醒大家,運行程序之前一定要先存檔。

5. c語言練習指針,任意給5個數,逆序輸出

#include<stdio.h>
void change(int *p);
main()
{
int a[5];
int *p;
printf("請輸入5個數字\n");
for(p=a;p<a+5;p++)
scanf("%d",p);
change(a);
}
void change(int *p)
{
int i=0,j=4,temp;
for(;i<2;i++,j--)
{
temp=*(p+i);
*(p+i)=*(p+j);
*(p+j)=temp;
}
for(i=0;i<5;i++)
printf("%d",*p++);// 輸出值 要用取值 *,否則輸出地址
printf("\n");//分號掉了
}

6. C語言關於指針的三道簡單選擇題,來個大神解釋下,謝謝

第一題明顯D是答案
第二題明顯ACD數組越界錯誤 正確答案為B
第三題A x[3]為3無疑問,B p1為x[3]的地址 ++是後++不需考慮 也是3 C p1為x[2]地址,後面的++無論括弧與否都是表達式執行完最後執行,D 先++之後也是x[3]的地址 明顯正確。
所以只有C錯誤 答案為C

這個題庫正確答案都給不出來,顯然不值得你去練習,建議換地方做題~
第二題那麼明顯的大小為10的數組下標最多為9這種題目都答案給錯,可見出題者的水平,第一題無論怎樣都不可能離開C和D 答案居然為B太侮辱智商。不必糾結了 想學習指針去看C指針與陷阱這本書

7. 三道C語言題目,求大神(用指針的方法)。

(1)

#include"stdio.h"
intmain(intargv,char*argc[]){
intn,i,k;
printf("Inputn()):");
if(scanf("%d",&n)!=1||n<1||n>9999){
printf("%disoutofrangeofvalidvalues. ",n);
return0;
}
k=0;
for(i=1;i<=n;i++)
k=(k+3)%i;
printf("LastNo.is:%d ",k%n+1);
return0;
}

運行樣例:

8. C語言指針指向函數編程練習

#include "stdio.h"//
void fun0(int n){
printf("這是%d號函數的列印輸出。\n",n+1);
}
void fun1(int n){
printf("這是%d號函數的列印輸出。\n",n+1);
}
void fun2(int n){
printf("這是%d號函數的列印輸出。\n",n+1);
}
void main(void){
void (*ptr[3])(int)={fun0,fun1,fun2};
int n;
printf("Type 1,2 or 3\nn=");
scanf("%d",&n);
n--;
ptr[n](n);
printf("\n");
}

9. C語言指針練習 列印不出結果

首先,來觀察一下 long *tpre(char pref)函數,該函數中的switch語句語法
switch ( controllingExpression ) {
case constantExpression1 :
...
default : statements ; break;
}
沒有默認的出口,因此你在調用它時傳入一個大寫的XYZ ABC都無法識別,默認函數返回一個空指針。
其次,你在調用完之後試圖使用語句 if (&a == (long **)*a) printf("A is lucky!\n");列印結果,對一個NULL指針做取址操作,結果肯定列印不出來。假如你輸入的是abc xyz在函數中有判斷的字元,是可以正常列印結果的。

10. c語言指針程序題,程序如下,答案是3,6。我想知道為什麼答案是3,6而不是6,3希望您能幫我詳細解答一下。

呵呵,你被主函數中的a,b和子函數中的a,b所混淆了!
(1)主函數中的a,b是變數名,a=3,b=6,內容為3和6 。
(2)子函數中的a,b是指針變數,是指向整型變數的指針變數【(int * a,int * b)】。k也是指針變數,調用子函數fun時【fun(x,y)】,是將a,b的地址傳過來了,不是a,b的值!在子函數內部,
k=a;a=b;b=k;交換的是就是的地址(注意:這里的a,b實際上是x,y,也就是主函數中的&a,&b),也就是將主函數中的變數地址交換了而已,就是將變數換個地方而已,變數名和變數的值都沒有變!
指針變數是C語言中比較難的一個知識點,尤其是指向指針的指針、指向多維數組的指針。希望你多看書,多練習,認真領會。

熱點內容
java返回this 發布:2025-10-20 08:28:16 瀏覽:582
製作腳本網站 發布:2025-10-20 08:17:34 瀏覽:876
python中的init方法 發布:2025-10-20 08:17:33 瀏覽:571
圖案密碼什麼意思 發布:2025-10-20 08:16:56 瀏覽:757
怎麼清理微信視頻緩存 發布:2025-10-20 08:12:37 瀏覽:673
c語言編譯器怎麼看執行過程 發布:2025-10-20 08:00:32 瀏覽:1000
郵箱如何填寫發信伺服器 發布:2025-10-20 07:45:27 瀏覽:244
shell腳本入門案例 發布:2025-10-20 07:44:45 瀏覽:103
怎麼上傳照片瀏覽上傳 發布:2025-10-20 07:44:03 瀏覽:795
python股票數據獲取 發布:2025-10-20 07:39:44 瀏覽:701