garbage output after whitespace removing - c

i've this code:
int i =0;
char * str = "ar bitrary whitespace";
int whitespace=0,index;
for(index = 0;index < strlen(str);index++)
{
if(isspace(str[index]) != 0)
{
whitespace++;
}
}
char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));
memset(tmp,'\0',strlen(tmp)+1);
while(i < strlen(str))
{
if(isspace(str[i]) != 0)
{
i++;
continue;
}else if(isspace(str[i]) == 0)
{
strcat(tmp,&str[i]);
i++;
}
}
printf("\nnew string is: %s \n",tmp);
the problem is that the output is a string without the whitespace removed + some garbage character.
I've used memset to null terminate tmp,is there the problem?

The length of the source string could be calculated before this loop
for(index = 0;index < strlen(str);index++)
Otherwise if the code will not be optimized the function strlen will be called for each iteration of the loop. In fact using of the function is redundant for such a task.
This statement
memset(tmp,'\0',strlen(tmp)+1);
does not make sense because the call of calloc already initialized the memory with zeroes.
This statement
strcat(tmp,&str[i]);
also copies blanks from the source string after the position i. So it can write beyond the memory allocated for the array pointed to by the pointer tmp.
You can write a separate function that can look as it is shown in this demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
char * remove_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) ++n;
} while ( *p++ );
char *t = malloc( n );
if ( t )
{
char *q = t;
p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
} while ( *p++ );
}
return t;
}
int main(void)
{
char * str = "ar bitrary whitespace";
printf( "\"%s\"\n", str );
char *t = remove_blanks( str );
printf( "\"%s\"\n", t );
free( t );
}
The program output is
"ar bitrary whitespace"
"arbitrarywhitespace"

this is your problem
memset(tmp,'\0',strlen(tmp)+1);
strlen(tmp) works by looking for '\0' in tmp, you have a chicken and egg situation here.
You should not be doing a memset any way, just tack on a '\0' when you fnish copying
And dont use strcat, instead maintain a pointer to tmp and just do *p = str[i] then increment p

I will not read your question, you overwrite the '\0' terminator for sure.
Now that I read your question, it looks like you need to understand strings and arrays better,
Don't ever write while (i < strlen(str))
Don't use strcat() for adding a single character, you apparently did overwrite the '\0' there. Furthermore, don't ever use strcat() for concatenating more than to pieces of a string.
Also notable,
You memset() after calloc() which already initialized to 0. That means that you are enforcing something that is not necessary, and trying it twice as if it failed the first time which I can guarantee it didn't.
In fact, since you have used calloc() and all bytes pointed to by tmp are 0 then strlen(tmp) will return 0, thus your memset() is equivalent to
tmp[0] = '\0';
and you REALLY don't need initialize tmp except when you finally copy the actual bytes from str.
I always advice against calloc() for strings, because
You don't really need to initialize something twice.
You should be sure your code does take the terminating '\0' into account and not simply assume that it's there because you calloc()ed. That is a bug that you just hide with calloc() but it shows up at some point.
Try this and see if you can understand the reasons for my changes
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void)
{
int whitespace;
int length;
char *str = "ar bitrary whitespace";
char *tmp;
whitespace = 0;
for (length = 0; str[length] != '\0'; ++length) {
if (isspace(str[length]) != 0) {
whitespace++;
}
}
tmp = malloc(length - whitespace + 1);
if (tmp == NULL)
return -1;
for (int i = 0, j = 0; str[i] != '\0'; ++i) {
if (isspace(str[i]) != 0)
continue;
tmp[j++] = str[i];
}
tmp[length - whitespace] = '\0';
printf("new string is: %s\n",tmp);
free(tmp);
return 0;
}

Related

reduce the size of a string

(disclaimer: this is not a complete exercise because I have to finish it, but error occurred in this part of code)
I did this exercise to practice memory allocation.
create a function that takes an url (a C string) and returns the name of the website (with "www." and with the extension).
for example, given wikipedia's link, "http://www.wikipedia.org/", it has to return only "www.wikipedia.org" in another string (dynamically allocated in the heap).
this is what I did so far:
do a for-loop, and when "i" is greater than 6, then start copying each character in another string until "/" is reached.
I need to allocate the other string, and then reallocate that.
here's my attempt so far:
char *read_website(const char *url) {
char *str = malloc(sizeof(char));
if (str == NULL) {
exit(1);
}
for (unsigned int i = 0; url[i] != "/" && i > 6; ++i) {
if (i <= 6) {
continue;
}
char* s = realloc(str, sizeof(char) + 1);
if (s == NULL) {
exit(1);
}
*str = *s;
}
return str;
}
int main(void) {
char s[] = "http://www.wikipedia.org/";
char *str = read_website(s);
return 0;
}
(1) by debugging line-by-line, I've noticed that the program ends once for-loop is reached.
(2) another thing: I've chosen to create another pointer when I've used realloc, because I have to check if there's memory leak. Is it a good practice? Or should I've done something else?
There are multiple problems in your code:
url[i] != "/" is incorrect, it is a type mismatch. You should compare the character url[i] with a character constant '/', not a string literal "/".
char *s = realloc(str, sizeof(char) + 1); reallocates only to size 2, not the current length plus 1.
you do not increase the pointers, neither do you use the index variable.
instead of using malloc and realloc, you should first compute the length of the server name and allocate the array with the correct size directly.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *read_website(const char *url) {
// skip the protocol part
if (!strncmp(url, "http://", 7))
url += 7;
else if (!strncmp(url, "https://", 8))
url += 8;
// compute the length of the domain name, stop at ':' or '/'
size_t n = strcspn(url, "/:");
// return an allocated copy of the substring
return strndup(url, n);
}
int main(void) {
char s[] = "http://www.wikipedia.org/";
char *str = read_website(s);
printf("%s -> %s\n", s, str);
free(str);
return 0;
}
strndup() is a POSIX function available on many systems and that will be part of the next version of the C Standard. If it is not available on your target, here is a simple implementation:
char *strndup(const char *s, size_t n) {
char *p;
size_t i;
for (i = 0; i < n && s[i]; i++)
continue;
p = malloc(i + 1);
if (p) {
memcpy(p, s, i);
p[i] = '\0';
}
return p;
}
The assignment doesn't say the returned string must be of minimal size, and the amount of memory used for URLs is minimal.
Building on chqrlie's solution, I'd start by finding the beginning of the domain name (skipping the protocol portion), duplicate the rest of the string, and then truncate the result. Roughly:
char *prot[] = { "http://", "https://" };
for( int i=0; i < 2; i++ ) {
if( 0 == strncmp(url, http, strlen(prot)) )
s += strlen(prot);
break;
}
}
char *output = strdup(s);
if( output ) {
size_t n = strcspn(output, "/:");
output[n] = '\0';
}
return output;
The returned pointer can still be freed by the caller, so the total "wasted" space is limited to the trailing part of the truncated URL.

Allocating memory to user input using malloc [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am trying to dynamically allocate memory for this C program I have constructed which takes a single string input and reverses it then prints the reversed output. I am very new to C and am unsure on how to use malloc to allocate memory for specified user input. This is the current code I have, we are running these programs in linux, using gcc code.c -o code to compile. Where the file is named code.c
int main(void)
{
char input[256];
char temp;
int i = 0, j = 0;
scanf("%s", input);
j = strlen(input) - 1;
while (i < j)
{
temp = input[j];
input[j] = input[i];
input[i] = temp;
i++;
j--;
}
printf("Reverse = %s", input);
return 0;
}
For a malloc implementation you just need to replace
char input[256];
with
char* input = malloc(256);
if(input == NULL){
//uppon bad allocation NULL will be returned by malloc
perror("malloc");
return EXIT_FAILURE;
}
Since malloc needs the allocated space in bytes normally you would multiply the needed space by the type of variable:
char *input = malloc(256 * sizeof *input);
but, since the size of a char is always 1 byte, in this case you won't need it.
The rest of the code can be the same, then after you use input you can/should free the allocated memory:
free(input);
You will need to #include <stdlib.h>.
Side note:
To avoid buffer overflow it's recommmended that you define a maximum size for scanf specifier which sould be at most the size of the buffer:
scanf("%255s", input);
or to also parse spaces:
scanf("%255[^\n]", input);
Notice that there is one less space, which is reserved for the null terminator, automatically added by scanf and mandatory for the char array to be treated as a string.
Once you get a string from user, you can allocate memory like this.
malloc() returns a pointer to allocated memory, or NULL if it failed.
size_t input_len = strlen(input);
char* mem_data = malloc(input_len + 1); // +1 for null-terminate
if(!mem_data)
{
// failed to allocate memory. do dome error process
}
Following code is copying input reversely to the allocated memory.
int i;
for (i = 0; i < input_len; ++i)
{
mem_data[i] = input[input_len - i - 1];
}
mem_data[input_len] = 0; // null terminate
printf("Reverse = %s", mem_data);
Remember, dynamically allocated memory should be free()d after use.
free(mem_data); // never forget to free memory when you finished using.
To dynamically allocate space in c you need to know beforehand exactly how much space you want to allocate. So, allocate space exactly same size as input string, you need to take length of the string as input.
Sample code will be like below:
int n;
char* input;
int main(){
scanf("%d",&n);
input= (char*)malloc((n+1)*sizeof(char));
scanf("%s",input);
return 0;
}
You can read about dynamic memory allocation here https://www.geeksforgeeks.org/dynamic-memory-allocation-in-c-using-malloc-calloc-free-and-realloc/
The approach that is more often used in books on C for beginners is writing a function that reads character by character from the input stream and reallocates memory dynamically when a new character is read.
Something like the function shown in this demonstrative program below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * getline( size_t n )
{
char *s = NULL;
if ( n != 0 )
{
s = malloc( sizeof( char ) );
size_t i = 0;
for ( int c; i < n - 1 && ( c = getchar() ) != EOF && c != '\n'; ++i )
{
s = realloc( s, i + 2 );
if ( s == NULL ) break;
s[i] = c;
}
if ( s ) s[i] = '\0';
}
return s;
}
char * reverse( char *s )
{
if ( *s != '\0' )
{
for ( size_t i = 0, j = strlen( s ); i < --j; i++ )
{
char c = s[i];
s[i] = s[j];
s[j] = c;
}
}
return s;
}
int main(void)
{
const size_t N = 256;
printf( "Enter a string (no more than %zu symbols): ", N );
char *s = getline( N );
if ( s ) printf( "The reversed string is \"%s\"\n", reverse( s ) );
free( s );
return 0;
}
The program output might look like
Enter a string (no more than 256 symbols): Hello Mgert33
The reversed string is "33tregM olleH"

Assert is failing in a string array comparison [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 3 years ago.
In this part of my code I remove white spaces of string1 and copy the result to string2.
char * remove_blank_spaces(char * string1) {
char * string2 = malloc(sizeof(string1));
int index = 0;
for (int i = 0; string1[i] != 0; i++) {
if(string1[i] != ' ') {
//printf("i: %d\n", i);
//printf("c2: %c\n", string1[i]);
string2[index] = string1[i];
index++;
}
}
string2[index] = '\0';
printf("string2: %s\n", string2);
return string2;
}
I check the result with:
assert(remove_blank_spaces("a b") == "ab"); // Edit: here is the error!
I got an error: Assertion failed! and Expression: remove_blank_spaces("a b") == "ab"
I compared the strings in Virtual-C and they look the same.
Why the assertion is failing?
Your code has a bug: malloc allocates insufficient space, and this results in undefined behaviour when trying to access unallocated memory.
The assertion is also failing because you are comparing pointers via ==, instead of C strings via strcmp.
Furthermore, I suggest making two changes:
Don’t mix computation and output. Return the value, don’t printf it inside the function.
Use descriptive and correct names. This requires taking context into account. For instance, index can generally be a good name, but in your case it’s unclear which index you’re referring to, and this invites errors, where index is used to index into the wrong variable. As for “correct” names, what you call “blank space” is more conventionally known as “whitespace”.
To improve the second point, I suggest actually changing the implementation and, instead of having a second index variable, to iterate over the output using a pointer. There are other possibilities, but this one has the advantage that accidentally indexing using the wrong variable is impossible.
Taking this together, we get
char *remove_whitespace(const char *str) {
char *result = malloc(strlen(str) + 1);
char *out = result;
for (size_t i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
*out++ = str[i];
}
}
*out = '\0';
return result;
}
We could additionally do away with the i loop counter. Unfortunately the result is less readable, not more, because we would need to increment str at the end of the loop, and this would leave us with an unsightly for (; *str != '\0'; str++) loop construct.
For starters this function declaration
char * remove_blank_spaces(char * string1) {
is incorrect and only confuses users of the function. If within the function you are creating a new character array then the parameter shall have the qualifier const.
char * remove_blank_spaces( const char * string1) {
Otherwise the function should change the original string "in-place".
This call
char * string2 = malloc(sizeof(string1));
also is incorrect. I think you mean
char * string2 = malloc( strlen( string1 ) + 1 );
But even this call is not very good because the result string can be much less than the original string.
So at first you should count the numb er of characters in the result string and only then allocate the memory.
This assert is also incorrect
assert(remove_blank_spaces("a b") == "ab");
In this expression there are compared addresses of two string: the first one is the string returned by the function and the second one is the string literal.
Even if you will write an expression like this
assert( "ab" == "ab");
the value of the expression can be equal either to logical true or false depending on the compiler option that specifies whether equal string literals are stored as one string literal or occupy different extents of memory.
You should write instead
assert( strcmp( remove_blank_spaces("a b"), "ab" ) == 0 );
Take into account that it is reasonable also to consider trhe tab character '\t' in the if statement like
if(string1[i] != ' ' && string1[i] != '\t') {
Or you could use the standard function isblank.
Here is a demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( const char *s )
{
size_t n = 0;
for ( size_t i = 0; s[i] != '\0'; i++ )
{
if ( !isblank( ( unsigned char )s[i] ) ) ++n;
}
char *result = malloc( n + sizeof( ( char )'\0' ) );
char *p = result;
do
{
if ( !isblank( ( unsigned char )*s ) )
{
*p++ = *s;
}
} while ( *s++ != '\0' );
return result;
}
int main(void)
{
const char *s1 = "a b";
char *s2 = remove_blank_spaces( s1 );
assert( strcmp( s1, s2 ) == 0 );
puts( s2 );
free( s2 );
return 0;
}
The program output is
ab
Pay attention to that instead of the type int as it is shown in other answers you should use the type size_t for the variables index and i because it is the type that is used with string lengths and indices and by the function malloc. The type int is not large enough to store size of strings.
If you indeed want to declare the function like
char * remove_blank_spaces( char *s )
that is when the parameter does not have the qualifier const then you shall not allocate dynamically a new character array within the function and the function itself can look much simpler.
Here is a demonstrative program.
#include <stdio.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( char *s )
{
char *destination = s;
char *source = s;
do
{
if ( *source != ' ' && *source != '\t' )
{
*destination++ = *source;
}
} while ( *source++ != '\0' );
return s;
}
int main(void)
{
char s[] = "a b";
remove_blank_spaces( s );
assert( strcmp( s, "ab" ) == 0 );
puts( s );
return 0;
}
Its output is
ab

Removing spaces from strings

I tried to write a function that gets a string and creates a new string but without multiple spaces (leaving only 1 space between words).
So far I wrote this, but for some reason it crashs and the debugger shows nothing.
I also don't know where do I need to put the free function...
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* upgradestring(char* oldtext);
int main()
{
char str1[] = "Chocolate Can Boost Your Workout" ;
printf("%s\n", str1);
printf("\n%s\n", upgradestring(str1));
return 0;
}
char* upgradestring(char* oldtext)
{
int i,j, count = 1;
char *newstr;
for (i = 0; oldtext[i] != '\0'; i++)
{
if (oldtext[i] != ' ')
count++;
else if (oldtext[i - 1] != ' ')
count++;
}
newstr = (char*)malloc(count * sizeof(char));
if (newstr == NULL)
exit(1);
for (i = 0, j = 0; (oldtext[i] != '\0')|| j<(count+1); i++)
{
if (oldtext[i] != ' ')
{
newstr[j] = oldtext[i];
j++;
}
else if (oldtext[i - 1] != ' ')
{
newstr[j] = oldtext[i];
j++;
}
}
return newstr;
}
You're addressing [i-1] and it's not within the range of the original array if i==0.
Here's how you could do it:
Simply copy one by one and if the char is ' ', keep skipping while it is ' ', otherwise advance by one.
static size_t newlen(char const *o)
{
size_t r=0;
while(*o){
r++;
if(*o==' ')
while(*o==' ')
o++;
else
o++;
}
return r;
}
char *upgradestring(char const *o)
{
char *r, *p;
size_t len = newlen(o);
if( 0==(r = malloc(len+1)))
return 0;
r[len]=0;
for(p=r;*o;){
*p++=*o;
if(*o==' ')
while(*o==' ')
o++;
else
o++;
}
return r;
}
int main()
{
char str1[] = "Chocolate Can Boost Your Workout" ;
char *new;
printf("%s\n", str1);
if(0 == (new = upgradestring(str1)))
return 1;
printf("%s\n", new);
free(new);
}
Failures to allocate are best signalled by return codes (you wouldn't want a library function to abort your program if it fails).
In order to be able to free the returned string, you first must capture it in a variable.
Good attempt, but let's focus on when you need to free your memory. You allocate dynamically the memory inside the function, then call the function inside a printf, which will allow the string to print, but how will you deallocate it? Use a pointer to assign the return value of your function, print it, and then free it!
Moreover, you need to allocate space for as many characters the new string has, plus one for the null terminator, since C strings require that to work smoothly with functions coming from headers, such as printf().
Furthermore, we do not cast what malloc() returns in C, read more here.
Also this:
else if (oldtext[i - 1] != ' ')
should be written as:
else if (i != 0 && oldtext[i - 1] != ' ')
to avoid accessing oldtext[-1] which is out of bounds, when i is 0.
Lastly, the condition you used when populating the new string, would be better with a logical AND, instead of an OR, since we have to stop as soon as either condition is false (we do not want to read past the null terminator of the original string, or past the size of the new string).
Putting everything together, we:
#include <stdio.h>
#include <stdlib.h>
char* upgradestring(char* oldtext)
{
int i, j, count = 0;
// compute 'count'
for(i = 0; oldtext[i]; i++)
{
if (oldtext[i] != ' ')
count++;
else if (i != 0 && oldtext[i - 1] != ' ')
count++;
}
char* newstr = malloc(count + 1); // PLUS ONE for the null terminator
if(!newstr) // check if malloc failed
{
printf("Malloc failed\n");
return 0;
}
// populate 'newstr'. We need to stop when either condition is false
for (i = 0, j = 0; (oldtext[i]) && j<(count+1); i++)
{
// Same as your code
}
// Assign the null terminator!
newstr[j] = '\0';
return newstr;
}
int main(void) {
char str1[] = "Chocolate Can Boost Your Workout" ;
// store the result of your function into 'newstr'
char* newstr = upgradestring(str1);
// print it
printf("%s\n", newstr);
// free it, since you no longer need it!
free(newstr);
return 0;
}
Output:
Chocolate Can Boost Your Workout
#include <stdio.h>
#include <stdlib.h>
char *upgradestring(char *oldtext)
{
size_t len,src,dst,spc;
char *result;
// First pass: count needed size
for (len=src=spc=0;oldtext[src]; src++){
if (oldtext[src] != ' ') spc=0; // non-space needs space
else if(spc++) continue; // skip non first space
len++;
}
result= malloc (1+len);
// Second pass: copy(K&R style)
for (dst=src=spc=0; (result[dst] = oldtext[src]) ; src++){
if (oldtext[src] != ' ') spc=0; // non-space: rest counter
else if(spc++) continue; // skip non-first space
dst++;
}
return result;
}
Simplified version: dont calculate the size in a first pass, but start with the same size as the original, and resize after the second pass. (strdup() can be replaced by strlen+malloc+memcpy)
char * strdup(char *);
char *upgradestring2(char *oldtext)
{
size_t src,dst,spc;
char *result;
result= strdup (oldtext);
// edit the copy, skipping all spaces except the first
for (dst=src=spc=0; result[src] ; src++){
if (result[src] != ' ') spc=0; // non-space:reset counter
else if(spc++) continue; // skip space,except the first
result[dst++] = result[src]; // Copy
}
result[dst] = 0;// terminate string;
// result=realloc(result, dst+1);
return result;
}
For starters neither declaration from the header <string.h> is used in your program. Thus this directive
#include <string.h>
may be removed from the program.
According to the C Standard the function main without parameters shall be declared like
int main( void )
The function with the strange name upgradestring:) does not change the argument. Hence it should be declared like
char* upgradestring( const char* oldtext);
^^^^^
Take into account that the source string can start with blanks. In this case statements like this
else if (oldtext[i - 1] != ' ')
count++;
result in undefined behavior because there is an attempt to access memory beyond the string when i is equal to 0.
The condition
(oldtext[i] != '\0')|| j<(count+1);
should be written at least like
(oldtext[i] != '\0') && j<(count+1);
^^^
though it is enough to check the index j because it can not be greater than the length of the source string.
You forgot to append the result string with the terminating zero '\0'.
Also it is not a good idea to exit the function with this statement
exit(1);
In this case you could just return a null pointer.
And the allocated memory should be freed before exiting the program.
As it has been mentioned before a source string can start with spaces and also have a redundant trailing space. I think it will be logically consistent to exclude them from the result string.
Usually the space character is considered in pair with the tab character. Moreover C has a special function isblank declared in the header <ctype.h> that checks whether a character is a space or a blank. (As far as I know the MS VS does not support this function)
Taking all this into account the function can be defined the following way as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
char * trim_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
// skip leading blanks
while ( *p == ' ' || *p == '\t' ) ++p;
_Bool last_blank = 0;
for ( ; *p; ++p )
{
++n;
if ( ( last_blank = ( *p == ' ' || *p == '\t' ) ) )
{
while ( p[1] == ' ' || p[1] == '\t' ) ++p;
}
}
if ( last_blank ) --n;
char *q = malloc( n + 1 );
if ( q )
{
p = s;
// skip leading blanks
while ( *p == ' ' || *p == '\t' ) ++p;
size_t i = 0;
for ( ; i < n; i++, ++p )
{
q[i] = *p == '\t' ? ' ' : *p;
if ( q[i] == ' ' )
{
while ( p[1] == ' ' || p[1] == '\t' ) ++p;
}
}
q[i] = '\0';
}
return q;
}
int main(void)
{
char s[] = "\t\tChocolate \t Can \t Boost Your Workout ";
printf( "\"%s\"\n", s );
char *t = trim_blanks( s );
printf( "\"%s\"\n", t );
free( t );
return 0;
}
The program output is
" Chocolate Can Boost Your Workout "
"Chocolate Can Boost Your Workout"

Allocating multidimensional char pointer in C

I am trying to read lines from a file and store them in a multidimensional char pointer. When I run my code, it runs without errors, however, the lines will not be printed correctly in my main() function, but prints correctly in the getline() function. Can anybody explain what is happening and how I correctly store them in my multidimensional pointer?
My code:
int main (int argc, const char * argv[]) {
char **s = (char**) malloc(sizeof(char*) * 1000);
int i = 0;
while (getline(s[i]) != -1){
printf("In array: %s \n", s[i]);
i++;
}
return 0;
}
int getline(char *s){
s = malloc(sizeof(char*) * 1000);
int c, i = 0;
while ((c = getchar()) != '\n' && c != EOF) {
s[i++] = c;
}
s[i] = '\0';
printf("String: %s \n", s);
if (c == EOF) {
return -1;
}
return 0;
}
and my output:
String: First line
<br>In array: (null)
<br>String: Second line
<br>In array: (null)
<br>String: Third line
<br>In array: (null)
You are passing by value. Even though you change the value of *s in getline(), main does not see it. You have to pass the address of s[i] so that getline() can change it:
int getline(char **s) {
* s= malloc( sizeof(char) * 1000) ;
...
}
Also, if you want to be a bit more efficient with memory, read the line into a local buffer (of size 1000) if you want. Then when you are done reading the line, allocate only the memory you need to store the actual string.
int getline( char ** s )
{
char tmpstr[1000] ;
...
tmpstr[i++]= c ;
}
tmpstr[i]= '\0' ;
* s= strdup( tmpstr) ;
return 0 ;
}
If you want to improve things even further, take a step back and thing about a few things. 1) allocating the two parts of the multi-dimensional array in two different functions is going to make it harder for others to understand. 2) passing in a temporary string from outside to getline() would allow it to be significantly simpler:
int main()
{
char ** s= (char **) malloc( 1000 * sizeof(char *)) ;
char tmpstr[1000] ;
int i ;
while ( -1 != getline( tmpstr))
{
s[i ++]= strdup( tmpstr) ;
}
return 0 ;
}
int getline( char * s)
{
int c, i = 0 ;
while (( '\n' != ( c= getchar())) && ( EOF != c )) { s[i ++]= c ; }
s[i]= '\0' ;
return ( EOF == c ) ? -1 : 0 ;
}
Now, getline is just about IO, and all the allocation of s is handled in one place, and thus easier to reason about.
The problem is that this line inside getline function
s = malloc(sizeof(char) * 1000); // Should be sizeof(char), not sizeof(char*)
has no effect on the s[i] pointer passed in. This is because pointers are passed by value.
You have two choices here:
Move your memory allocation into main, and keep passing the pointer, or
Keep your allocation in getline, but pass it a pointer to pointer from main.
Here is how you change main for the first option:
int main (int argc, const char * argv[]) {
char **s = malloc(sizeof(char*) * 1000);
int i = 0;
for ( ; ; ) {
s[i] = malloc(sizeof(char) * 1000);
if (getline(s[i]) == -1) break;
printf("In array: %s \n", s[i]);
i++;
}
return 0;
}
My advise: completely avoid writing your own getline() function, and avoid all fixed size buffers. In the POSIX.1-2008 standart, there is already a getline() function. So you can do this:
//Tell stdio.h that we want POSIX.1-2008 functions
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
int main() {
char** s = NULL;
int count = 0;
do {
count++;
s = realloc(s, count*sizeof(char*));
s[count-1] = NULL; //so that getline() will allocate the memory itself
} while(getline(&s[count-1], NULL, stdin));
for(int i = 0; i < count; i++) printf(s[i]);
}
Why do you use malloc in the first place. malloc is very very dangerous!!
Just use s[1000][1000] and pass &s[0][0] for the first line, &s[1][0] for the second line etc.
For printing printf("%s \n", s[i]); where i is the line you want.

Resources