出産前後の痔にはご注意!

どうにも動的確保について間違っている気がするのでお尋ねいたします。
よくメモリを動的に確保する場合に私は次のようなプログラムを書きます

ポインタを用意する(例えばint *p)
mallocでメモリ確保
ポインタを関数に渡す(Func(p))
関数側でreallocし、値を代入する(p=(int *)realloc(p,sizeof(int)*num))
関数呼び出し側で、その値を使う

しかし、この方法を使うとどうにも関数を呼び出した際のポインタ(p)と、reallocした後のポインタ(p)の値が違うことがあり、値が不定になることがあります。
(reallocのメモリの確保の仕方のせいでしょうか)
この使い方は恐らくどこか間違っていると思うんですが、いまいち納得のいく解決策が思いつきません。
例えばポインタを引数ではなく戻り値として得ればできますが、2つ以上のポインタについてはできません。

何卒ご教授のほどをよろしくお願いいたします。

このQ&Aに関連する最新のQ&A

A 回答 (5件)

まず、p=(int *)realloc(p,sizeof(int)*num);


で確保された領域が、それ以前の p と同じであることは保障されていません。
realloc() で保障されているのは、「新旧の領域の、短い長さまでの内容は同じである」ということだけです。

この場合、ポインタの値自体は、値渡しになりますから、例えば、

void func(int **ptr);

int *p = malloc();
// ただし、関数内部で realloc() する前提なら、
// 呼び出し前に、確保する必要性はないかも
func(&p);
....

void func(int **ptr)
{
*ptr = realoc();
...
}

でいけたような気がします(未チェック)

あと、最近は、
(p=(int *)realloc(p,sizeof(int)*num))
と alloc() 系の関数の返値をキャストする必要はありません。
大昔は、alloc() 系は char * を返していましたが、今は、 void * を返すので、これは、キャストなしで任意のポインタに安全に変換できます。

この回答への補足

ありがとうございます。
なるほど、**ptrを使うことで関数呼び出し側にも反映されるんですね。

ただ、テストプログラムを作ってみると、何やらまた動作がおかしいみたいです
おかしい部分を抜き出したソースは次のとおりです

int main()
{
 int **p;
 int i;

 p = (int **)malloc(sizeof(int *));
 *p = (int *)malloc(sizeof(int));

 for(i=1;i<10;i++){
  *p = (int *)realloc(*p,sizeof(int)*(i+1));
  *p[i] = i;
 }
 free(*p);
 return 0;
}

このようにするとreallocがメモリ領域を拡張してくれなく(?)、*p[i] = i;の部分でエラー終了します。
主題が変わって(?)しまって恐縮ですが、まだ何かおかしいのでしょうか。
もし宜しければお願いいたします。

補足日時:2007/06/19 17:27
    • good
    • 0

> ポインタを用意する(例えばint *p)


> mallocでメモリ確保
> ポインタを関数に渡す(Func(p))
> 関数側でreallocし、値を代入する(p=(int *)realloc(p,sizeof(int)*num))
> 関数呼び出し側で、その値を使う

この場合、Func内部で常にメモリを解放するか、返却値等でpの値を返す必要がありますね。

> しかし、この方法を使うとどうにも関数を呼び出した際のポインタ(p)と、reallocした後のポインタ(p)の値が違うことがあり、値が不定になることがあります。

reallocでは、内部で新しいメモリブロックを割り付けることがありますので、返却値が元のポインタと同じ値になるとは限りません。通常、メモリブロックのサイズを広げるときには、ポインタの値が変わります。
なお、reallocで再割り付けを行うときは次にようにします。

int *t = realloc(p, size);
if (t != NULL)
 p = t;
else
 /* エラー処理 */

こうしないと、reallocが割り付けに失敗したとき、元のポインタの値が失われ、結果としてメモリリークにつながります。
また、C言語ではmallocやreallocの返却値はキャスト不要ですが、C++ではキャストが必須ですので、必要に応じて読み替えてください。ちなみに、C++ではreallocを使うよりstd::vectorなどを使う方が便利です。

> 例えばポインタを引数ではなく戻り値として得ればできますが、2つ以上のポインタについてはできません。

複数を扱う場合は、返却値にするより、結果を格納する配列を引数として渡した方が便利です。というか、元の値を配列で渡して、変化後の値を同じ配列に入れて返してあげればよいのです。
    • good
    • 0
この回答へのお礼

ありがとうございます。
参考になりました。

エラー処理については気をつけたいと思います。

お礼日時:2007/06/19 18:22

#3です。



サンプルコードのコメントが
間違っていたので訂正します。
//pはallocの『中身』
 ↓
//pはallocのコピー
もう一つ。
//pはallocへのポインタ
 ↓
//pはallocへのポインタのコピー
です。
参照渡しとか言われますが、厳密には、
C言語では、ポインタを値渡しします。
    • good
    • 0

>ポインタを関数に渡す(Func(p))


提示されたコードはやりがちですが、
非常に危険で間違ったプログラムです。

>2つ以上のポインタについてはできません。
これは
int** sub( void );
のようなことを言っているのでしょうか?
であれば可能ですが今回はあまり触れません。

以下は危険なコードサンプル
#include <stdio.h>
#include <stdlib.h>
int sub( int* p );
int main( int argc, char* argv[] )
{
 int ret;
 int *alloc;
 //allocをわざと初期化していません。
 ret = sub( alloc );
 if( ret ){
  return -1;
 }
 //allocの中身は変わっていない為、不定
 printf( "alloc = %#x\n", alloc );
 //不定領域を開放
 free( alloc );
 return 0;
}

int sub( int* p )
{
 //pはallocの『中身』
 //pに確保領域のアドレスが入る
 p = (int*)malloc( sizeof(int)*10 );
 if( !p ){
  return -1;
 }
 printf( "p = %#x\n", p );
 //関数終了と共にpが破棄される
 return 0;
}

上記のように引数に対して戻り値を設定する場合、
ポインタを渡さなければなりません。
上記のコードを修正するならば、
以下のようになります。
#include <stdio.h>
#include <stdlib.h>
int sub( int** p );
int main( int argc, char* argv[] )
{
 int ret;
 int *alloc;

 //allocへのポインタを渡す
 ret = sub( &alloc );
 if( ret ){
  return -1;
 }
 //allocの中身は確保された領域へのポインタ
 printf( "alloc = %#x\n", alloc );
 //正しい場所を開放
 free( alloc );

 return 0;
}

int sub( int** p )
{
 //pはallocへのポインタ
 //allocへのポインタの中身に確保領域のアドレスが入る
 *p = (int*)malloc( sizeof(int)*10 );
 if( !p ){
  return -1;
 }
 printf( "p = %#x\n", *p );

 //関数終了と共にpが破棄される
 return 0;
}

上記のコードはインデントを全角スペースにしているので、
コンパイルする時は半角に置換してください。
    • good
    • 0
この回答へのお礼

ありがとうございます。
参考になりました。

2つというのは語弊がありました。
2つ以上のポインタについて、reallocしたい場合と言う意味です

お礼日時:2007/06/19 18:21

メイン側でmallocしたのに、関数側でもreallocするのですか?


それなら、メイン側ではポインタだけ提示して、関数側でmallocすればよいのではないでしょうか?
C言語から離れて10余年なので、間違っているかもしれませんが(笑)

main(){
int *p;
// ポインタを関数に渡す(ポインタにmallocされたアドレスが欲しいのでポインタのポインタを渡す)
Func(&p);
}

Func(int **p){
*p = malloc(~);
}
    • good
    • 0
この回答へのお礼

メイン側でしていたのは、Func(p)の場合だとしていない場合エラーしてしまうためでした。
&p、**pを使うところまでは分かりました。
あとはrealloc・・・
別件で再質問することにいたします。

ありがとうございました。

お礼日時:2007/06/19 18:19

このQ&Aに関連する人気のQ&A

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Q配列の動的確保を関数化

main()内でいくつかの配列を動的確保するとごちゃごちゃするので、
関数化してみました。しかし、下のプログラムではうまい事いきませ
んでした。

#include<stdio.h>
#include<stdlib.h>

void array1d(int *box,int n)
{
int i;
box=(int *)malloc(n * sizeof(int));
for(i=0; i<n; i++) { box[i]=0; }
}

main()
{
int *pol,i,n;
printf("N pol\nN = ");
scanf("%d",&n);
array1d(pol,n);
for(i=0; i<n; i++) { printf("[%d]=%d\n",i,pol[i]); }
}

実行結果は
[0]=-14646387
[1]=-1819410433
[2]=-224
array1d内では0を格納していますが、このようになりました。
どのようにすれば 0になるでしょうか?
回答よろしくお願いします。

main()内でいくつかの配列を動的確保するとごちゃごちゃするので、
関数化してみました。しかし、下のプログラムではうまい事いきませ
んでした。

#include<stdio.h>
#include<stdlib.h>

void array1d(int *box,int n)
{
int i;
box=(int *)malloc(n * sizeof(int));
for(i=0; i<n; i++) { box[i]=0; }
}

main()
{
int *pol,i,n;
printf("N pol\nN = ");
scanf("%d",&n);
array1d(pol,n);
for(i=0; i<n; i++) { printf("[%d]=%d\n",i,pol[i]); }
}

実行結果は
[0]=-1...続きを読む

Aベストアンサー

もっと素直に考えた方がいいですよ。
動的にとるなら、関数内で確保すればいいのでは?
但しメモリの確保開放は入り口と出口が明示的にわかった方がいいので、あまり関数を分けて確保したり、開放したりするのは私的にお勧めできません。

ちょっと質問者のソースをいじってみました。
これでよいのではないですか?

#include<stdio.h>
#include<stdlib.h>

//動的に確保するなら別に引数に渡さず確保した先頭アドレスだけ返せば良い!
int *array1d(int n)
{
int i;
int *box;

box=(int *)malloc(n * sizeof(int));

for(i=0; i<n; i++)
{
box[i]=0;
}

return box;
}

main()
{

int *pol,i,n;

printf("N pol\nN = ");

scanf("%d",&n);

pol = array1d(n);

for(i=0; i<n; i++)
{
printf("[%d]=%d\n",i,pol[i] );
}

//必ず開放して下さい。
free(pol);
}

もっと素直に考えた方がいいですよ。
動的にとるなら、関数内で確保すればいいのでは?
但しメモリの確保開放は入り口と出口が明示的にわかった方がいいので、あまり関数を分けて確保したり、開放したりするのは私的にお勧めできません。

ちょっと質問者のソースをいじってみました。
これでよいのではないですか?

#include<stdio.h>
#include<stdlib.h>

//動的に確保するなら別に引数に渡さず確保した先頭アドレスだけ返せば良い!
int *array1d(int n)
{
int i;
int *box;

box=(int *)mall...続きを読む

QLNK2019: 未解決の外部シンボルのエラーが出る

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自キャラのデータ
Point2D g_jikipos = {40, 400};//自キャラの座標

//画像ハンドル
int g_jikiimage[11];

//色々なファイルの読み込み
int LoadFiles(){
//画像ファイル読み込み
if(LoadDivGraph("media\\player01.bmp",
11,11,1,64,64,g_jikiimage) == -1) return -1;

return 1;
}


 mymain.h
//他から呼び出させるMyMainの関数
void MyMain();
int LoadFiles();


 myhelper.h(サンプルなので打ちミスはない)
#include "DxLib.h"
#include <limits.h>
#include <math.h>

//構造体宣言
//座標またはベクトルを記録する構造体
struct Vector{
float x,y;
};
typedef Vector Point2D;
//線を記録する構造体
struct Line2D{
Point2D startpos, endpos;
float katamuki;//傾きをラジアン値で記録
Vector speed;//移動している場合は速度をセット
};
//球体を記録する構造体
struct Ball2D{
Point2D position;
float hankei;//半径
};
//四角形を記録する構造体
struct Rect2D{
Point2D lefttop;
Point2D rightbottom;
float width;
float height;
};


//ライブラリ関数
Point2D PosInView(Point2D in);
int XInView(float inx);
int YInView(float iny);
void ScrollToLeft(float jikiposx);
void ScrollToRight(float jikiposx);
void ScrollToUp(float jikiposy);
void ScrollToDown(float jikiposy);
void DrawLineInView(float x1, float y1, float x2, float y2, int Color, int Thickness);
void DrawCircleInView(float x, float y, float r, int Color, int FillFlag);
void DrawAnimation(float x, float y, double ExtRate, double Angle,int TurnFlag,
int *imgarray, int allframe, float fps);
//ベクトル関数
Vector CreateVector(Vector in, float veclen);
Vector AddVector(Vector v1, Vector v2);
Vector SubVector(Vector v1, Vector v2);
Vector AddVectorInFrameTime(Vector pos, Vector speed);
Vector AddVectorInFrameTime2(Vector pos, Vector speed, Vector accel);
Vector Normalize(Vector in);
Vector RotateVector(Vector in, float radian);
float VectorLengthSquare(Vector in);
float DotProduct(Vector v1, Vector v2);
float CrossProduct(Vector v1, Vector v2);
void SetLine2DKatamuki(Line2D *in);
void DrawLine2D(Line2D in, int Color, int Thickness);
void DrawBall2D(Ball2D in, int Color, int Fill);
//当たり判定関数
bool HitTestLineAndBall(Line2D linein, Ball2D ballin);
bool IsPointAtLineFace(Line2D linein, Point2D ptin);
bool HitTestLineAndLine(Line2D line1, Line2D line2);
bool HitTestBallAndBall(Ball2D a, Ball2D b);
bool HitTestPointAndBox(Rect2D rect, Point2D pt);
//タイマー関数
void SetSimpleTimer(int idx, int time);
int GetPassedTime(int idx);


//グローバル変数
extern float g_frametime;
extern Rect2D g_framerect;//画面領域(当たり判定)
extern Point2D g_current_field_pos;//現在の左上座標
extern Rect2D g_stagesize;//ステージサイズ

//定数宣言
const float ZEROVALUE = 1e-10f;
const float PIE = 3.1415926f;
const int SCROLL_LIMIT = 200;
----------------------------------------------------------------
 エラー内容
1>myhelper.obj : error LNK2019: 未解決の外部シンボル "void __cdecl MyMain(void)" (?MyMain@@YAXXZ) が関数 _WinMain@16 で参照されました
1>C:\Documents and Settings\Owner\My Documents\Visual Studio 2008\Projects\my\Debug\my.exe : fatal error LNK1120: 外部参照 1 が未解決です
1>my - エラー 2、警告 0
ビルド: 0 正常終了、1 失敗、0 更新不要、0 スキップ
----------------------------------------------------------------
画像を貼り付けときます
(見えにくい場合→http://www.dotup.org/uploda/www.dotup.org154142.jpg.html)
初心者なのでわかりやすくお願いします

Microsoft Visual Studio 2008
Version 9.0.21022.8 RTM
Microsoft .NET Framework
Version 3.5 SP1
----------------------------------------------------------------
新しいプリジェクト→Win32 コンソール アプリケーション(ソリューションのディレクトリを作成 チェック外す)→Windows アプリケーション(空のプロジェクト チェック外す)
----------------------------------------------------------------
 プログラム

 mymain.cpp
#include "myhelper.h"
#include "mymain.h"

//自...続きを読む

Aベストアンサー

ファイル構成から推測するに
mymain.cpp というファイルに
void MyMain(void) {
// ここに処理を書く
}
という関数が必要なようです。

Q動的配列の受け渡しについて

動的に定義した2次元配列を受け渡す方法がわからなくて困ってます。

配列に-1以外が入っている場合の行数を数えるプログラムを作成しています。
しかし、コンパイルすると下のエラーが出てコンパイルできません。
error:cannot convert 'long int **' to 'long int (*)[2] for argument '1' to 'long int count_line(long int (*)[2])'

#include <stdio.h>

long count_line(long arrau[][2]);

int main(void){
  long** test = new long* [100];
  for(long num1=0 ; num1<100 ; num1++){
    test[num1] = new long [2];
  }
  for(int i=0 ; i<100 ; i++){
    test[i][0]=-1;
    test[i][1]=-1;
  }

  for(int num2=0 ; num2<10 ; num2++){
    test[num2][0]=1;
    test[num2][1]=1;
  }

  int x=0;
  x = count_line(test);
  printf("x=%d\n",x);
}

long count_line(long array[][2]){
  long count=0;
  long end=0;
  long roop=0;
  while(end!=-1){
    if(array[roop][1]!=-1){
      count++;
    }
    end=array[roop][1];
    roop++;
  }
  return(count);
}


どなたかお助けをー(T_T)

動的に定義した2次元配列を受け渡す方法がわからなくて困ってます。

配列に-1以外が入っている場合の行数を数えるプログラムを作成しています。
しかし、コンパイルすると下のエラーが出てコンパイルできません。
error:cannot convert 'long int **' to 'long int (*)[2] for argument '1' to 'long int count_line(long int (*)[2])'

#include <stdio.h>

long count_line(long arrau[][2]);

int main(void){
  long** test = new long* [100];
  for(long num1=0 ; num1<100 ; num1++){
 ...続きを読む

Aベストアンサー

>long count_line(long arrau[][2]);

配列arrauは、メモリ上に以下のように並んでいる事になります。

arrau {long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
(以下略、個数不明)

>  long** test = new long* [100];

一方、testは、以下のようになります。

test{long *(どこかのアドレス1を指している)}

どこかのアドレス1 {long *(どこかのアドレス2を指している)}
どこかのアドレス1+?{long *(どこかのアドレス3を指している)}
どこかのアドレス1+?{long *(どこかのアドレス4を指している)}
(以下、100個、繰り返し)

どこかのアドレス2{long long}

どこかのアドレス3{long long}

どこかのアドレス4{long long}
(以下、不連続にバラバラに100個存在する)

「どっからどう見ても、メモリ上のデータの並び方が違う」とは思いませんか?

コンパイラは「データの並びが全然違うから、引数を言われた形には変換できません。無理っす」って言ってエラーを吐きます。

>どなたかお助けをー(T_T)

「引数の型」と「実際に引数に渡している変数の型」を一致させて下さい。

そうすれば、エラーもワーニングも消えます。

>long count_line(long arrau[][2]);

配列arrauは、メモリ上に以下のように並んでいる事になります。

arrau {long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
arrau+?{long long}
(以下略、個数不明)

>  long** test = new long* [100];

一方、testは、以下のようになります。

test{long *(どこかのアドレス1を指している)}

どこかのアドレス1 {long *(どこかのアドレス2を指している)}
どこかのアドレス1+?{long *(どこかのアドレス3を...続きを読む

Qfgetsで拾われる改行文字を削除したい

お世話になります

 C言語初心者のものです。今課題でC言語を用いたプログラミングを
Fedora上でやっています。問題は、fgetsでテキストファイルから、取得
した文字列の中から改行文字を削除できないことです。文字変数のアド
レスはわかっているのですが、終端文字に置換しようとすると、セグメ
ントエラーになってしまいます。これは如何にして解決すべきでしょう
か。よろしくお願いします。

Aベストアンサー

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが最大文字数に達したり、ファイルの最後になったりで、strに改行文字が含まれない場合には、このループは止まりません(Segmentension Falutになって止まる)

・そのような状態になってないか、予めチェックする
・ループを終了させる仕組みを用意しておく
: forの終了条件を記述する、for中で if(*(str+i)=='\0') { break;} 等としておく、等
といった対策が必要です。


あと細かいところを言えば
・strを配列で用意したなら *(s+i)じゃなくてs[i]でいいんじゃないかな
・あるいは char *pみたいにしておいて、 iのループでなく pでループを組む( for(p=str;*p!='\0';p++) )とか。

ポインタとかアドレスとか、C言語の用語としてあるものを別の意味に使うとまぎらわしいです。

「ポインタ」「アドレス」と言われたら、 この例なら str, str+i が思い浮びます。
「文字変数のアドレス」だと
char c ;
に対しての
&c
が思い浮びます。

配列なら「添字」、意味的には「x文字目」ですね。

> for(i=0;;i++){
> if(*(str+i)=='/n') {
> *(str+i)='\0';
> break;
> }
> }
/nが\nの間違いなら、この方法で半分正解です。もう少し広い範囲(可能なら全体)で見ないことにはなんとも言えません。
fgetsが...続きを読む

Q戻り値で構造体を返すことは可能でしょうか?

perlでは以下のように2つの戻り値が可能ですが、C言語では
それができるのでしょうか?
my (ret1, ret2) = test1();

よくやるのは、引数にポインタを渡して、内容を書き換える手を使っていますが、戻り値を複数返せたら、直感的にわかりやすいかなと思いまして・・・

Aベストアンサー

C言語から遠く離れた者ですが、

>>> よくやるのは、引数にポインタを渡して、内容を書き換える手を使っています

これが常識でしょう。これが直感的に理解できるようにC言語を身に付ける必要があるのではないでしょうか。

QC言語で文字列をかえす正しい書き方が知りたいです?

C言語で次の警告が表示されます。
文字列を返したいのですが、正しい書き方はどのようにすれば良いのでしょうか?


jci.h(20) : warning C4172; ローカル変数またはテンポラリのアドレスを返します。


char *test(char *a, int b)
{
char str[BUFSIZ];
return str; <------

}

Aベストアンサー

再入可能にするかどうかで、回答は変わります。

A.2度呼び出した場合に前のデータを破壊してもよいケース(再入不可能)

char *test(...)
{
static char str[BUFSIZ]; // static指定でメモリは静的に確保されます。

...

return str;
}

B.2度呼び出した場合に前のデータを破壊しないケース(再入可能)
B-1.mallocを使ってもいいケース
char *test(...)
{
char *str;
str = malloc(BUFSIZ);
if(str == NULL) return NULL; // エラー

...

return str;
}
この場合は、呼び出し元でちゃんとfreeしましょう。

B-2.呼び出し元でメモリを確保するケース
(注意:同じアドレスを指定して複数回呼び出すと、メモリ内容は当然破壊されます)
char *test(char *str, ...)
{

...

return str;
}
これは#1の方の回答と同じです。

B-3.B-1/B-2の複合
(注意:NULL以外の同じアドレスを指定して複数回呼び出すと、メモリ内容は当然破壊されます)
char *test(char *str, ...)
{
if(str == NULL)
{
str = malloc(BUFSIZ);
if(str == NULL) return NULL; //エラー
}

...

return str;
}

こんなところですかね。

再入可能にするかどうかで、回答は変わります。

A.2度呼び出した場合に前のデータを破壊してもよいケース(再入不可能)

char *test(...)
{
static char str[BUFSIZ]; // static指定でメモリは静的に確保されます。

...

return str;
}

B.2度呼び出した場合に前のデータを破壊しないケース(再入可能)
B-1.mallocを使ってもいいケース
char *test(...)
{
char *str;
str = malloc(BUFSIZ);
if(str == NULL) return NULL; // エラー

...

return str;
}
この場合は、呼び...続きを読む

QC言語で、メモリを解放しないで終わるプログラム

C言語の話です。

mallocなどで領域を確保したら、解放しなければいけないんですよね。
しかし、解放しないで終了すると具体的にどうなるのか、私は理解していません。

次のような、freeしないプログラムを作って何回か実行してみました。しかし、別におかしくならないですね。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *cp;

int main(void)
{
unsigned int n;

printf("サイズ(2以上)を入力してください:");
scanf("%d",&n);

cp=malloc(n);
if(!cp)
{
printf("%s\n","mallocできませんでした。");
return(1);
}

strcpy(cp,"A");
printf("cpは%sです。\n", cp);

printf("それでは終わりにします\n");

return(0);
}


グローバルでcharの固定長の配列を宣言したとすれば、プログラムの終了時にその領域は解放されると思います。

このような固定長の配列の場合とmallocの場合との違いが問題なんです。

 実験的に、解放しないがために何かおかしくなってしまったり、悪影響を及ぼしたりするようなプログラムを作りたいんですが、どのようにすればよいでしょうか。

もしも私の環境ではそのようなプログラムが作れないなら、別の環境の話でもよいので具体的にこんなふうになってしまうという話をお聞きしたいんです。


過去の質問を検索してみました。
http://oshiete1.goo.ne.jp/kotaeru.php3?q=160037
ここのNo.9では、「freeしないアプリケーションの起動・終了を繰り返すと、リソースが不足する」旨が書かれていて、質問者の方もそれで納得されているようです。
しかし、私は、リソースが不足するとはどういうことで、何が起こるのか、知りません。

私のマシン
OS:Windows98SE
VC++6.0

C言語の話です。

mallocなどで領域を確保したら、解放しなければいけないんですよね。
しかし、解放しないで終了すると具体的にどうなるのか、私は理解していません。

次のような、freeしないプログラムを作って何回か実行してみました。しかし、別におかしくならないですね。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *cp;

int main(void)
{
unsigned int n;

printf("サイズ(2以上)を入力してください:");
scanf("%d",&n);

cp=malloc(n);
if(!cp)
{...続きを読む

Aベストアンサー

書き忘れましたのでもう一度。

malloc/freeを1セットで考えている人がいるようですが、「プログラム終了時にすべてをfreeする」なんていう処理は愚の骨頂です。

malloc/freeはマンションの内装工事のようなものです。
mallocは次の入居者のために内装工事を行います。
freeは次の入居者のための回復工事です。
マンション自体の取り壊し(=プログラムの終了)が行われる寸前に回復工事の必要はありません。

動的メモリ領域を10000個くらい確保して、freeして終了する場合とfreeせずに終了する場合を比べてみてください。
プログラム終了寸前にfreeするのは無駄以外の何者でもありません。

Q文字列配列をメンバにもつ構造体の,メモリ動的確保

下に同様の質問があるのですが,やはり理解できません.
文字列配列をメンバにもつ構造体の,メモリを動的に確保をしたいのですが,うまくいきません.
具体的には以下のようです.
正しくはどのようにすればよいでしょうか.よろしくお願いします.

typedef struct {
char **boy;
char **girl;
} Name_class;

int init_name_class(Name_class name_class, int n_boy, int n_girl)
{
int i;

name_class.boy = (char**) malloc( n_boy * sizeof(char**) );
for(i=0; i<n_boy; i++)
name_class.boy[i] = (char*) malloc( 32 * sizeof(char*) );

name_class.girl = (char**) malloc( n_girl * sizeof(char**) );
for(i=0; i<n_girl; i++)
name_class.girl[i] = (char*) malloc( 32 * sizeof(char*) );
}


main(int argc, char *argv[])
{
int i, j, n_boy=3, n_girl=2;
Name_class name_class;

init_name_class(name_class, n_boy, n_girl);

name_class.boy[0] = "yukio";
name_class.boy[1] = "hideaki";
name_class.boy[2] = "mitsuru";

name_class.girl[0] = "youko";
name_class.girl[1] = "chiharu";

printf("%s, %s, %s, %s, %s\n", name_class.boy[0], name_class.boy[1], name_class.boy[2], name_class.girl[0], name_class.girl[1]);
}

下に同様の質問があるのですが,やはり理解できません.
文字列配列をメンバにもつ構造体の,メモリを動的に確保をしたいのですが,うまくいきません.
具体的には以下のようです.
正しくはどのようにすればよいでしょうか.よろしくお願いします.

typedef struct {
char **boy;
char **girl;
} Name_class;

int init_name_class(Name_class name_class, int n_boy, int n_girl)
{
int i;

name_class.boy = (char**) malloc( n_boy * sizeof(char**) );
for(i=0; i<n_boy; i++)
...続きを読む

Aベストアンサー

×name_class.boy = (char**) malloc( n_boy * sizeof(char**) );
○name_class.boy = (char**) malloc( n_boy * sizeof(char*) );
メンバboyは、char**のサイズの領域をn_boy個ではなく、char*のサイズの領域をn_boy個欲しい筈。

×name_class.boy[i] = (char*) malloc( 32 * sizeof(char*) );
○name_class.boy[i] = (char*) malloc( 32 * sizeof(char) );
boy配列の各要素は、char*サイズの領域が32個分ではなく、charサイズの領域が32個分、つまりは32文字分の領域が欲しい筈。

以下、girlメンバについても同様。

更に、
init_name_class(name_class, n_boy, n_girl);
で動的にメモリ確保したのに
name_class.boy[0] = "yukio";
name_class.boy[1] = "hideaki";
name_class.boy[2] = "mitsuru";
name_class.girl[0] = "youko";
name_class.girl[1] = "chiharu";
とやってしまうと、動的に確保したメモリのポインタは忘れ去られ、スタティックな文字列へのポインタが配列要素に上書き代入されます。

そして、動的に確保したメモリは取り残され、だれも開放すること無く残ります。これをメモリリークと言います。

多分、free関数で開放してないのは「開放しようとするとメモリエラーで例外が発生してアプリが飛ぶ」からでしょうけど、動的に確保したメモリのポインタを書き潰してるので、メモリエラーが出て当然です。

strncpy(name_class.boy[0],"yukio",31);
name_class.boy[0][31]='\0';
strncpy(name_class.boy[1],"hideaki",31);
name_class.boy[1][31]='\0';
strncpy(name_class.boy[2],"mitsuru",31);
name_class.boy[2][31]='\0';
strncpy(name_class.girl[0],"youko",31);
name_class.girl[0][31]='\0';
strncpy(name_class.girl[1],"chiharu",31);
name_class.girl[1][31]='\0';
のように、(1つに付き32バイト分確保した)メモリに31文字以内で文字列コピーしましょう。

strncpyは、コピー時に指定したバイト数を超えそうになると終端文字の\0を書き込まないので、念の為32文字目に\0を書いておくようにしましょう。

また、mainが終る前に、free関数で確保したメモリを開放しましょう。開放の順番間違いや開放し忘れは、メモリリークの原因になります。

以下の2つのプログラムの違いが判りますか?

main()
{
 char *p;
 p = "test";
 printf("%s\n",p);
}

main()
{
 char *p;
 p = (char *)malloc(10 * sizeof(char));
 strncpy(p,"test",9);
 p[9]='\0';
 printf("%s\n",p);
 free(p);
}

以下の2つのプログラムは正しくありません。判りますか?(実行するとメモリエラーで例外終了します)

main()
{
 char *p;
 p = (char *)malloc(10 * sizeof(char));
 p = "test";
 printf("%s\n",p);
 free(p);
}

main()
{
 char *p;
 strncpy(p,"test",9);
 p[9]='\0';
 printf("%s\n",p);
}

×name_class.boy = (char**) malloc( n_boy * sizeof(char**) );
○name_class.boy = (char**) malloc( n_boy * sizeof(char*) );
メンバboyは、char**のサイズの領域をn_boy個ではなく、char*のサイズの領域をn_boy個欲しい筈。

×name_class.boy[i] = (char*) malloc( 32 * sizeof(char*) );
○name_class.boy[i] = (char*) malloc( 32 * sizeof(char) );
boy配列の各要素は、char*サイズの領域が32個分ではなく、charサイズの領域が32個分、つまりは32文字分の領域が欲しい筈。

以下、girlメンバについ...続きを読む

Q配列の要素数に変数を入れたいときには

よろしくお願いします。
配列の要素数には定数しか入れられないのですが,どうしても変数を入れたいときは,それを引数として関数を呼び出すしか方法はないでしょうか。
具体的には,scanfで手に入れたint型の変数を要素数とする配列を宣言したいのですが,どうすれば良いでしょうか。
ご教授ください。

Aベストアンサー

c99と呼ばれる最近の規格では、配列の大きさに変数を使用できます。
bccはc99に対応していないようです。

それ以前の規格では、動的領域確保関数 malloc や callocを使って領域を確保するか、効率等を無視してバカデカい配列を用意しておくかです。
「それを引数として関数を呼び出す」っていうのは、malloc/callocのことですか?

Qint型からchar型への変換

タイトル通り、int型からchar型への変換の仕方がわかりません!><
どうしたらいいのでしょうか?

Aベストアンサー

#include <stdio.h>


char buf[5];
int no;

no = 10;
sprintf(buf, "%d", no);


人気Q&Aランキング