再現
注:すべてのトピックは私が今まで見てきたネットワークだけでなく、インタビューの質問に要約され、追加してください!
下記の特別な例は、プログラムの下で、すべての質問のメッシュのLinux 32ビットのCではありません。
まず、いくつかの簡単なまで昇温。
計算値はsizeof 1、。
チャーSTR1 [] = { 'A'、 'B'、 'C'、 'D'、 'E'}; チャーSTR2 [] = "ABCDE";
チャー* PTR = "ABCDE";
CHAR予約[] [80 ] = {「コンピュータアプリケーションの基礎」、「C言語」、「C ++プログラミング」、「データ構造」}。
sizeof(STR1)=?
sizeof(STR2)=?
sizeof(PTR)=?
sizeof(書籍)=?
sizeof(ブック[0])=?
分析:
sizeof(STR1)= 5は、5 *のはsizeof(CHAR)= 5です。
sizeof(STR2)= 6、文字列に基づいている「\ 0」末端、6によって占有されるバイト数。
sizeof(PTR)= 4、PTRは、プラットフォーム32のサイズにポインタがある4バイトです。
sizeof(本)= 320は、ブックが2次元アレイであり、4 * 80 *
sizeof(ブック[0])= 80、ブック[0]は、配列の最初の次元である80 * 1ため
sizeof必要な配列要素の数は、とり、また、最初は非常に簡単である(0009)はsizeofする/はsizeof(CHAR)。
図2は、上記の計算は、それらが占めるバイト数を求めている、のは、文字列または配列の需要の方法を実際の長さを見てみましょう。STRLEN値は以下の計算します。
チャーarryA [] = { 'A'、 'B'、 'C'、 '\ 0'、 'D'、 'E'}。
チャーarryB [] = { 'A'、 'B'、 'C'、 'D'、 'E'}。
チャーarryC [6] = { 'A'、 'B'、 'C'、 'D'、 'E'}。
char * strの= "ABCDE";
分析:
STRLEN(arryA)= 3は、strlenを出会い '\ 0' どのように多くの文字をバックに関係なく、返されません。
STRLEN(arryB)長さを決定することができない計算終わりに見出されるまで、strlenを「0 \」は1つの書き込みは、続行しないであろう、結果は不明です。
STRLEN(arryC)= 5は、アレイのサイズ、コンパイラが自動的に追加さを指定するスペア領域 '\ 0'、そのチャーarryC [6] = { 'A'、 'B'、 'C'、 'D' は実際には、 'E'、 '\ 0'};と等価。
strlenの末尾に '\ 0' は含まない(文字列)= 5、。
上記の二つから、我々はのsizeofとstrlenの違いを見てみましょう。
(1)はsizeofは、C言語の単項演算子、類似++である - 等。
例えばはsizeofなどのデータ、はsizeof(型)のタイプ(INT)
変数はsizeof(VAR_NAME)
注:はsizeof機能タイプを使用、または不完全型ビットフィールドすることができません。不完全型は、未知のサイズのアレイ型ストレージとして、未知のタイプのデータ記憶サイズを指し、
不明なコンテンツ構造体または共用タイプ、void型。例えば:はsizeof(MAX)、このとき変数は最大のint MAX()として定義されている場合、はsizeof(char_v)、この時間char_v
これはチャーchar_v [MAX]として定義され、MAXは不明です。
(2)、STRLENプロトタイプunsigned int型STRLEN(チャー*秒)である関数です。
計算strelnが「0 \」文字の配列に依存しなければならない、文字列によって文字が終了する決定。
3、ブラフ文字列str []とチャー* STR
(1)以下の操作をその正当な?間違って、その段階でだろうか?コンパイル時や実行時?
STRのchar [] = "こんにちは"; STR [0] = 'S'; //正当なIT のchar * STR = "こんにちは"; P [0] = 'S'; //正当何
分析:
どちらも、正常にコンパイルすることができるが、実行時の第2節では、意志のエラー。さんは分析してみましょう:
まず、「こんにちは」にコンパイル時に決定された静的データ領域(データ・セグメント)に格納された文字列定数です。最初は、可変(データセグメント内のグローバル変数、ローカル変数スタック領域)の文字列定数を代入することで、文字列定数は、実際のメモリ変数にコピーされ、したがって[]変数の唯一の修飾されたSTR値。
第二は、p-操作は文字列定数を変更することで、Pへの定数文字列の最初のアドレスを割り当てることです!だから、ミスがありました。
(2)は、以下の真または偽の判断を上記の知識を理解できますか?
チャー0009 [] = "ABC"。 チャーSTR2 [] = "ABC"。 CONSTチャーSTR3 [] = "ABC"。 CONSTチャーSTR4 [] = "ABC"。 constのchar * STR5 = "ABC"; constのchar * STR6 = "ABC"; char * STR7 = "ABC"; char * STR8 = "ABC"; COUT <<(STR1 == STR2)<< ENDL。 COUT <<(STR3 == STR4)<< ENDL。 COUT <<(STR5 ==化6)<< ENDL。 COUT <<(化7 == STR8)<< ENDL。
分析:
結果:0011
まずSTR1、STR2、STR3、STR4を理解し、彼らは何ですか?彼らは、配列の最初の要素のアドレスである配列の名前です!「STR1は== str2の」基本的に2つの配列を比較した対処同じではありません。私たちは、コンパイラは、互いに独立しており、これらの変数のメモリ内の文字列「ABC」を、コピーするために彼らに新しいストレージ容量を割り当て、そのアドレス確かに異なるので、上記の言いました!
すると化5、化11、化11、STR8を理解し、彼らは何ですか?彼らは、値が文字列定数のアドレスであるポインタ、です!彼らは、「ABC」が配置されている静的なデータ領域を指しているので、彼らは同じです。
(3)深い:次のプログラムは、問題がありますか?すべての問題はどこにありますか?どのように修正するには?
書式#include <stdio.hに>
CHAR * returnStr() { char型のP [] = "Hello Worldの!"。 Pを返します。 } int型のmain() { チャー*列str = NULL; STR = returnStr()。 printf( "%sのを\ n"、STR)。 0を返します。 }
分析:
pはローカル変数であるが、文字列「こんにちは言葉!」関数の終了時のローカル変数のコピーを運んがスタックに格納され、スタックが空である、pがリリースされますので、リリースされている返しますメモリアドレスは、これは間違っています。
これは次のように変更することができます。
書式#include <stdio.hに>
CHAR * returnStr() { char型* p = "こんにちは、世界!"; Pを返します。 }
int型のmain() { チャー*列str = NULL; STR = returnStr()。 printf( "%sのを\ n"、STR)。 0を返します。 }
問題がないので、エリアに格納されている「こんにちは世界!」静的データ、ポインタpと復帰に割り当てられたホームアドレスので、書かれた、でもreturnStr機能が終了した場合、それは文字列定数メモリ・ロケーションではないでしょう回復は、文字列定数にアクセスすることが可能です。
もちろん、あなたがそのように修飾することができます:
する#include <stdio.hの>
CHAR * returnStr() { 静的チャーP [] = "Hello Worldの!"。 Pを返します。 }
int型のmain() { チャー*列str = NULL; STR = returnStr()。 printf( "%sのを\ n"、STR)。 0を返します。 }
staticキーワードを使用して、静的ローカル変数は、それがメモリ空間を回復しないだろう、returnStr機能が終了した場合でも、データ・セグメントに変更されます。
図4に示すように、伝送パラメータの関数としてアレイ
私たちは、パラメータの関数として配列を置く傾向があるので、次の関数の問題を見ては何ですか。
INT FUNC(INT A [])
{ int型N =はsizeof(A)/はsizeof(INT)。 以下のために(iは++; iがn <I = 0 INT)
{ ( "%のD" [i])とのprintfと、 [I] ++; } }
nの値は常に1であることを見つけるためにのみ!なぜこれがそうですか?これは、配列を値渡しすることができないが、自動的にポインタとして縮重する関数に渡されるC、です。実際には、次の三つの文言は同じです。
"INTのFUNC([20] INT)" に相当する "([] INT)のint FUNC"; "int型FUNC(INT * A)" に相当します。
図5に示すように、これらのピットの数2つのExchange
次のコードは、2つの番号の交換を実現したいと考え、問題は何ですか?
ボイドスワップ(INT *、INT * B) { int型* pを、 P =; = B; B = P。 }
分析:
以下に示すように関数を呼び出すプログラムを実行し、この時点で渡されたスタックプッシュと新しい領域の割り当ての引数は、実際にはコピーです。
bの値との交流の値aとbのアドレスであるが、二つのアドレスのみを交換し、それはそれのコピーのアドレスを変更すると、アドレスが変更されていないオブジェクトを指摘!。
正しい方法はこれです:
ボイドスワップ(INT * A、INT * B) { int型TMP。 TMP = *; * A = * B。 * B = TMP; }
aとbがコピーであるが、直接関数の内部そのアドレスにオブジェクトの値を変更しているが、対応する引数は、変化を追跡します。
実際には、自然と値渡し渡さ渡されたポインタ値を渡すの値は、変速機のコピーに渡されます。コピーされ、引数のパラメータのアドレスは、いずれかの連絡先を持っていない、パラメータアドレスへの変更は、引数には影響を与えませんが、オブジェクトのパラメータアドレスへの変更は、直接引数に反映することができ指摘し、そのオブジェクトポイントがオブジェクト引数であるため、パラメータがあります。このような理由から、我々は、パラメータ、使用のconstが変更されるパラメータとしてポインタを渡すには、誤って変更されることからアドレスを防ぐためです。
図6は、ポインタ関数パラメータとして解釈されるべきです
次のコードで何が悪いのでしょうか?何が起こるか実行しますか?
ボイドGetMem(CHAR * P) { P =(CHAR *)のmalloc(100)。 } ボイドメイン() { チャー*列str = NULL; GetMem(STR)。 strcpyの(STR、 "こんにちは言葉!"); printf(STR)。 }
分析:
プログラムがクラッシュ。その上で、pはaddress引数strを気にしないパラメータを変更し、パラメータのコピーのみを転送するGetMemで、分析されました。ようにすると、空の文字列定数のアドレスにコピーし、NULLまだ、STRまたはSTR、必然的にクラッシュにつながります。次のメソッドは、この問題を解決することができます。
ボイドGetMem(チャー** P) { * P =(チャー*)のmalloc(100); } ボイドメイン() { CHAR * STR = NULL; GetMem(&STR); strcpyの(STR、 "こんにちはワード!"); のprintf (STR);
フリー(STR); //メモリリークフリー引き起こしません }
実際には十分に理解、少しあいまいなようです。基本的にポインタ変数strのに最初のアドレスに割り当てられている新しいmallocのメモリの先頭アドレスへのポインタ変数strのポイントを聞かせて。我々は、ポインタのSTR方法の値を変更することにより、本質的にSTR関数の値を変更するために、をstrを指すようにポインタを渡す必要があり、ポインタ値が渡され通過している、前に述べた、従ってサブ機能を転送するSTRのアドレスであり、そうしていますSTRに割り当てられたのmallocメモリの最初のアドレス。
図7に示すように、ポインタ疑問のアレイ
(1)以下の式の意味を教えて?
int型* P1 [10]。 INT(* P2)[10]。
最初は彼が配列され、すべての最初の配列へのポインタであり、要素がポインタです。
第二は、ポインタの配列である、すべての最初の彼は、配列へのポインタです。
下の画像は、はっきり説明することができます:
(2)次のプログラムの実行結果を書き込みます
INT [5] = {1、2、3、4、5}。 INT * PTR =(INT *)(&A + 1)。 printf( "%dを、%のD"、*(A + 1)*(PTR - 1))。
分析:
答案是2,5。本题的关键是理解指针运算,”+1“就是偏移量的问题:一个类型为T的指针移动,是以sizeof(T)为单位移动的。
a+1:在数组首元素地址的基础上,偏移一个sizeof(a[0])单位。因此a+1就代表数组第1个元素,为2;
&a+1:在数组首元素的基础上,偏移一个sizeof(a)单位,&a其实就是一个数组指针,类型为int(*)[5]。因此&a+1实际上是偏移了5个元素的长度,也就是a+5;再看ptr是int*类型,因此"ptr-1"就是减去sizeof(int*),即为a[4]=5;
a是数组首地址,也就是a[0]的地址,a+1是数组下一个元素的地址,即a[1]; &a是对象的首地址,&a+1是下一个对象的地址,即a[5]。
8、二级指针疑问
给定声明 const char * const *pp;下列操作或说明正确的是?
(A)pp++ (B)(*pp)++ (C)(**pp)=\\c\\; (D)以上都不对
分析:
答案是A。
先从一级指针说起吧:
(1)const char p : 限定变量p为只读。这样如p=2这样的赋值操作就是错误的。
(2)const char *p : p为一个指向char类型的指针,const只限定p指向的对象为只读。这样,p=&a或 p++等操作都是合法的,但如*p=4这样的操作就错了, 因为企图改写这个已经被限定为只读属性的对象。
(3)char *const p : 限定此指针为只读,这样p=&a或 p++等操作都是不合法的。而*p=3这样的操作合法,因为并没有限定其最终对象为只读。
(4)const char *const p :两者皆限定为只读,不能改写。
再来看二级指针问题:
(1)const char **p : p为一个指向指针的指针,const限定其最终对象为只读,显然这最终对象也是为char类型的变量。故像**p=3这样的赋值是错误的, 而像*p=? p++这样的操作合法。
(2)const char * const *p :限定最终对象和 p指向的指针为只读。这样 *p=?的操作也是错的,但是p++这种是合法的。
(3)const char * const * const p :全部限定为只读,都不可以改写
9、*p++、 (*p)++、 *++p、 ++*p
int a[5]={1, 2, 3, 4, 5};
int *p = a;
*p++ 先取指针p指向的值(数组第一个元素1),再将指针p自增1;
cout << *p++; // 结果为 1
cout <<(*p++); // 1
(*p)++ 先去指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2
cout << (*p)++; // 1
cout <<((*p)++) // 2
*++p 先将指针p自增1(此时指向数组第二个元素),* 操作再取出该值
cout << *++p; // 2
cout <<(*++p) // 2
++*p 先取指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2)
cout <<++*p; // 2
cout <<(++*p) // 2
参考博客:
1、《C语言中sizeof 与strlen 区别 》:http://www.cnblogs.com/kungfupanda/archive/2012/12/24/2831273.html
2、「なぜ、文字列定数は、静的なメモリ内に配置されましたか?「:HTTPS://blog.csdn.net/ebw123/article/details/51388340
3、「基準差によって渡さ転送の値、転送ポインタ、」:HTTPS://blog.csdn.net/koudan567/article/details/51298511
4、牛オフネットワークMLC答えます。https:?//Www.nowcoder.com/test/question/done TID = 16211084&QID = 1409#概要