Solved

malloc/array problems

Posted on 2006-10-28
17
280 Views
Last Modified: 2010-05-19
First off, yes this is a homework related question, BUT I only need help with one little piece of my code which I can't seem to make work right.  My understanding of EE rules is that this is acceptable.  So, onto the question:

We're rewriting some of the functions in <string.h> (oh the joys of programming 101 :D) using null-terminated character arrays.  One of the functions I'm trying to write will (hopefully) take a char* (uninitiated), a character, and a length and initiate the array filled with that character.  It compiles fine (using gcc -Wall -pedantic -ansi), but I keep getting run time errors when I try to execute it.

My function is:

int stringFill(char* str, char a, int len){
    int i;

    if((str = malloc(sizeof(char) * (len + 1))) == NULL) return 0;

    for(i=0; i<len; i++) str[i] = a;
    str[len+1] = '\0';

    return 1;
}

I call it like so:

char* string1; stringFill(string1, 'a', 20);
0
Comment
Question by:netsmithcentral
17 Comments
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17826996
You're losing the memory when the function is returned, the allocated memory has a local scope.

0
 
LVL 19

Accepted Solution

by:
BrianGEFF719 earned 225 total points
ID: 17827005
Allocate memory in the calling function:


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

int stringFill(char*, char, int);

void main( void )
{

      char *p;
      int n = 5;

      p = (char *) malloc( sizeof(char) * (n + 1));

      if ( stringFill(p,'a',n) )
            printf("%s",p);


      free(p);
}

int stringFill(char* str, char a, int len)
{
    int i;

    for(i=0; i < len; ++i)
        *(str + i) = a;

     *(str + len) = '\0'; //null terminate

    return 1;
}
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17827009
Also, if you use calloc() there is no need to null terminate:

p = (char *) calloc( (n + 1) , sizeof(char) ); //The char array is already initilized to zero.
0
PRTG Network Monitor: Intuitive Network Monitoring

Network Monitoring is essential to ensure that computer systems and network devices are running. Use PRTG to monitor LANs, servers, websites, applications and devices, bandwidth, virtual environments, remote systems, IoT, and many more. PRTG is easy to set up & use.

 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17827012
Here is an example of that:


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


int stringFill(char*, char, int);

void main( void )
{

     char *p;
     int n = 55;

     p = (char *) calloc( (n + 1) , sizeof(char) );

     if ( stringFill(p,'a',n) )
          printf("%s",p);


       printf("\nLen: %d", strlen(p));
     free(p);
}

int stringFill(char* str, char a, int len)
{
    int i;

    for(i=0; i < len; ++i)
       *(str + i) = a;

    return 1;
}
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17827036
Here is an example that demonstrates what is happening:


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

void pTest( char * );

void main( void )
{
      char *p;

      printf("p before = 0x%X\n",p);
      pTest(p);
      printf("p after = 0x%X\n",p);

}

void pTest( char *pC )
{
      pC = (char *) malloc (50);
      printf("p during pTest = 0x%X\n",pC);
}
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 17827283
Not quite Brian ... malloced memory remains in scope until it is freed by calling free.

int stringFill(char* str, char a, int len){
    int i;

    if((str = malloc(sizeof(char) * (len + 1))) == NULL) return 0;

    for(i=0; i<len; i++) str[i] = a;
    str[len+1] = '\0'; ---->>>>>>> For len+1 long array, valid indices are from 0 to len ... len+1 is writing beyond the array

    return 1;
}
0
 
LVL 45

Assisted Solution

by:sunnycoder
sunnycoder earned 225 total points
ID: 17827296
and ofcourse!!! You are assigning memory to a local variable ... Brian was on the right track indeed...

>int stringFill(char* str, char a, int len){
Inside the function, str is a local variable ... Any changes made to str will be visible only inside the stringFill function and NOT in the calling function. If you try to dereference the parameter in calling function, you would almost certainly get a crash.

You need to either pass in a char ** so that you can modify the char * variable used in calling function
int stringFill(char** strPtr, char a, int len)

OR

You need to return the new value of str ....
char * stringFill(char* str, char a, int len){
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17827298
>>Not quite Brian ... malloced memory remains in scope until it is freed by calling free.

But in this example you can clearly see that the char pointer p does not contain a valid memory address after the function returns.


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

void pTest( char * );

void main( void )
{
     char *p;

     printf("p before = 0x%X\n",p);
     pTest(p);
     printf("p after = 0x%X\n",p);

}

void pTest( char *pC )
{
     pC = (char *) malloc (50);
     printf("p during pTest = 0x%X\n",pC);
}
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 17827305
That is because char * pC is a local variable ... malloc'ed memory is in scope until it is free()d ... Since it cannot be freed in this case, this would be a memory leak!!!
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17827310
>>int stringFill(char** strPtr, char a, int len)

I didnt even think about that...a pointer to a pointer.
0
 
LVL 2

Expert Comment

by:numansiddique
ID: 17937354
The run time error is because of the statement
str[len+1] = '\0';

i agree with BrainGEFF719 , the variable has to be allocated memory before calling the function
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17940568
>>i agree with BrainGEFF719 , the variable has to be allocated memory before calling the function

SunnyCoder has shown that technically it doesnt need to be allocated in the calling function as long as you properly return the pointer and free() the memory in the calling function....I'd still personally allocate in the calling function simply for clarity.
0
 
LVL 12

Author Comment

by:netsmithcentral
ID: 17960641
Sorry it took so long to get back to this, my Linux box went down and I didn't have a chance until now to fix it (no Linux = no gcc = no coding for me).  I resolved the initial problem by allocating my memory in main()... I was wondering if you might be able to help me with another problem in the same code (posted the whole thing below).  For some reason, after the first scanf("%c", &srch) (which properly captures whatever character I type), I'm not prompted to enter a character.  Instead, the program always puts a newline character in as if I've typed it!

/*
 * CSC 101 Challenge - Lab 6
 *
 * String functions.  Legth: Return the length
 * of a null terminated string.  Fill: Fills a
 * given string with the given character. Equals
 * checks to see if two strings are the same and
 * prints the result. FindFirst: Finds and returns
 * the position of the first occurrence of a
 * character within a string.
 *
 * @author <<netsmithcentral>>
 *
 */

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

int stringLength(char* str);
void stringFill(char* str, char a, int len);
int stringEquals(char* str1, char* str2);
int stringFindFirst(char* str, char srch);
void stringWrite(char* str);

int main(){
    int cont = 1;

    char srch;
    int rep;

    char* string1; char* string2;
    string1 = (char*) malloc(sizeof(char) * 21);
    string2 = (char*) malloc(sizeof(char) * 21);

    stringFill(string1, 'a', 20); stringFill(string2, 'b', 20);

    printf("String 1 Length: %d\n", stringLength(string1));
    printf("String 2 Length: %d\n", stringLength(string2));
    printf("\n");

    do {
        printf("String 1 ");
          if(stringEquals(string1, string2)) printf("equals");
          else printf("does not equal");
        printf(" String 2.\n\n");

        printf("Enter a search character: "); scanf("%c", &srch);
        printf("Character is at %d in String 1 (-1 means character is not present).\n\n", stringFindFirst(string1, srch));

        printf("Enter position for new character (1 to %d): ", stringLength(string1)); scanf("%d", &rep);
        string1[rep-1] = srch;

        printf("String One's new contents: "); stringWrite(string1);
        printf("\n\n");

        printf("Continue (1 for yes, 0 for no): "); scanf("%d", &cont);
    } while(cont==1);

    free(string1); free(string2);
    return 0;
}

int stringLength(char* str){
    int i = 0;
    while(str[i]!='\0') i++;
    return i;
}

void stringFill(char* str, char a, int len){
    int i;

    for(i=0; i<len; i++) str[i] = a;
    str[len+1] = '\0';
}

int stringEquals(char* str1, char* str2){
    int i;

    if(stringLength(str1)!=stringLength(str2)) return 0;
    for(i=0; i<stringLength(str1); i++){
        if(str1[i]!=str2[i]) return 0;
    } return 1;
}

int stringFindFirst(char* str, char srch){
    int i;
    for(i=0; i<stringLength(str); i++){
        if(str[i]==srch) return i;
    } return -1;
}

void stringWrite(char* str){
    int i;

    for(i=0; i<stringLength(str); i++){
        printf("%c",str[i]);
    }
}
0
 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 17960751
You dont need to cast the malloc to a char pointer since malloc returns a charecter pointer by default.
You need to check if malloc was able to allocate the memory eg,

   if (string1 == NULL || string2 == NULL)
   {
    printf("unable to allocate memory");
    exit(1);
   }

Before the end of the while loop trying doing this sometimes scanf does weird things.

       int tmp;
       printf("Continue (1 for yes, 0 for no): "); scanf("%d",&tmp); scanf("%d", &cont);
    } while(cont==1);
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 17962588
>For some reason, after the first scanf("%c", &srch) (which properly captures whatever character I type), I'm not
>prompted to enter a character.  Instead, the program always puts a newline character in as if I've typed it!
Visualize input stream as a buffer ... In response to your first scanf, user inputs a value and presses <enter> key .... This value and \n are both placed in the input buffer ... your first scanf reads the value but leaves behind the \n ... your next scanf, reads this \n from the input buffer ... It does not block for input because it assumes that any value available in this buffer is the input!!

Work around - add additional scanf to clean all input buffer .... scanf also supports regular expressions and other capabilities in format specifiers which you can use to improve the robustness of your program ... It will be worth your time to spend some time reading its man page.

note that fflush (stdin) is undefined.

My prefered way of accepting user input is to use fgets to read in strings, validate them and then convert them to desired format.
0

Featured Post

Netscaler Common Configuration How To guides

If you use NetScaler you will want to see these guides. The NetScaler How To Guides show administrators how to get NetScaler up and configured by providing instructions for common scenarios and some not so common ones.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

An Outlet in Cocoa is a persistent reference to a GUI control; it connects a property (a variable) to a control.  For example, it is common to create an Outlet for the text field GUI control and change the text that appears in this field via that Ou…
This is a short and sweet, but (hopefully) to the point article. There seems to be some fundamental misunderstanding about the function prototype for the "main" function in C and C++, more specifically what type this function should return. I see so…
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use for-loops in the C programming language.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question