Function for scanning string - what's the problem? - c

char* scanString()
{
char* str = NULL;
char* temp = NULL;
int numOfChars = 0;
char c = '0';
while (c != '\n')
{
scanf(" %c", &c);
if (c != '\n')
{
if (numOfChars == 0)
{
char* str = (char*)malloc(sizeof(char));
char* temp = str;
if (str == NULL)
return str;
str[0] = c;
numOfChars++;
}
else
{
str = (char*)realloc(str, sizeof(char) * (numOfChars + 1));
str[numOfChars] = c;
if (str == NULL)
return temp;
else
{
temp = str;
numOfChars++;
}
}
}
}
str = (char*)realloc(str, sizeof(char) * (numOfChars+1));
if (str == NULL)
{
str = temp;
return str;
}
str[numOfChars] = '\0';
return str;
}
int main()
{
char* m;
printf("write:\n");
m = scanString();
printf("%s\n", m);
}
I tried to create a function for scanning a string of unknown size char by char and i don't know what'ss the problem here. Btw please don't approach me to any other code or try to use different libraries.

There is one big error and some inconsistencies.
The big error is here:
if (numOfChars == 0)
{
char* str = (char*)malloc(sizeof(char)); // Oops a new var!
char* temp = str; // and another one!
if (str == NULL)
return str;
str[0] = c;
numOfChars++;
}
You declare 2 new variables in that bloc that will hide the variables of outer scope. As a result, the first character will be lost and you will get a random value.
The inconsistencies:
temp is useless and should be removed
you read with a format " %c". The format will skip any blank character including \n. It should be "%c"
you fail to test the return value of scanf. On end of file (of any other read error) you will enter an endless loop. It should be:
if (1 != scanf("%c", &c)) break;
Once this is fixed, you should get the expected output, but other improvements are still possible:
the idiomatic way to read one character is getc or getchar
allocating one character at a time is an anti-pattern because (re-)allocation is a rather expensive operation. For a real world program, you should always allocate a bunch or memory and keep track of the available part
sizeof(char) is 1 per standard
the distinction for numOfChars == 0 is useless. realloc on a NULL pointer is the same of malloc.

Your code is overly complicated and wrong and as stated in a comment you need to replace the format string " %c" with "%c".
The main problem is here:
if (numOfChars == 0)
{
char* str = (char*)malloc(sizeof(char));
You declare a new str variable which shadows the str variable you've declared at the beginning of the function.
Just replace char* str = (char*)malloc(sizeof(char)) with str = malloc(sizeof(char));. BTW the cast is not necessary.
You have the same problem with temp.
The code below is entirely based on your code, but it is simpler and correct. Basically the case where numOfChars is 0 should not be treated specially. You can just use realloc, because realloc(NULL, foo) is equivalent to malloc(foo) .
char* scanString()
{
char* str = NULL;
char* temp = NULL;
int numOfChars = 0;
char c = '0';
while (c != '\n')
{
scanf("%c", &c);
if (c != '\n')
{
str = realloc(str, sizeof(char) * (numOfChars + 1));
if (str == NULL)
return temp;
str[numOfChars] = c;
temp = str;
numOfChars++;
}
}
str = realloc(str, sizeof(char) * (numOfChars + 1));
if (str == NULL)
{
str = temp;
return str;
}
str[numOfChars] = '\0';
return str;
}
Or even simpler:
char* scanString()
{
char* str = NULL;
char* temp = NULL;
int numOfChars = 0;
char c = '0';
while (c != '\n')
{
scanf("%c", &c);
str = realloc(str, sizeof(char) * (numOfChars + 1));
if (str == NULL)
return temp;
str[numOfChars] = c;
temp = str;
if (c == '\n')
{
str[numOfChars] = '\0';
}
numOfChars++;
}
return str;
}

Related

splitting a long dynamic string into an array of strings in c

I'm pretty new to C and can figure out why this function doesn't work consistently whatsoever:
char **splitString(char *string) {
char *token = strtok(string, ","), **finalValue = NULL, **temp = NULL;
size_t wordIndex = 0;
while (token != NULL) {
temp = realloc(finalValue, sizeof(char *));
if (!temp) {
freeArray(finalValue);
finalValue = NULL;
break;
}
temp[wordIndex] = malloc((strlen(token)+1)*sizeof(char));
if (temp[wordIndex] == NULL) {
freeArray(finalValue);
finalValue = NULL;
break;
}
strcpy(temp[wordIndex], token);
printf("%s\n", temp[wordIndex]);
finalValue = temp;
printf("%s\n", finalValue[wordIndex]);
wordIndex++;
token = strtok(NULL, ",");
}
return finalValue;
}
It receives a string separated by commas and its supposed to split them into different strings, all of which were created via malloc/realloc.
The problem is here: temp = realloc(finalValue, sizeof(char *)); reallocates for a single pointer. You should write:
temp = realloc(finalValue, (wordIndex + 2) * sizeof(char *));
You should also set a NULL pointer at the end of the finalValue array to mark the end of this array as the number of entries is not returned by the function in any other way.
Also note that the allocated strings are not freed when realloc() or malloc() fails.
Furthermore, you should not use strtok() because it modifies the source string. An alternative approach with strspn(), strcspn() or manual testing and strndup() is recommended.
Finally, strtok() has another shortcoming which may be counterproductive: it considers any sequence of separators as a single separator and does not produce empty tokens. This is fine if you use whitespace as separator but probably incorrect for "," where you might expect "a,,c" to produce 3 tokens: "a", "" and "c".
Here is a modified version that can handle empty tokens:
char **splitString(const char *string) {
const char *p0, *p0;
size_t i = 0, n = 1;
char **array;
for (p = string; *p; p++) {
if (*p == ',')
n++;
}
array = calloc(sizeof(*array), n + 1);
if (array != NULL) {
array[n] = NULL; /* set a null pointer at the end of the array */
for (p = p0 = string, i = 0; i < n;) {
if (*p == ',' || *p == '\0') {
if ((array[i++] = strndup(p0, p - p0)) == NULL) {
/* allocation failure: free allocated strings and array */
while (i --> 0)
free(array[i]);
free(array);
array = NULL;
break;
}
if (*p == ',')
p0 = ++p;
else
p0 = p;
} else {
p++;
}
}
}
return array;
}
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;
}

Reimplementing split function in C

I have been trying to write a function that takes in strings as a line and returns a pointer to an array of words. The function written below does something similar
How can I rewrite the following code1 but it should be better than code2 by being able to change the delimiter. However, code1 works but during memory allocation the same memory is duplicated for the words array. Thereby causing word duplication.
Code 1:
char *split(const char *string) {
char *words[MAX_LENGTH / 2];
char *word = (char *)calloc(MAX_WORD, sizeof(char));
memset(word, ' ', sizeof(char));
static int index = 0;
int line_index = 0;
int word_index = 0;
while (string[line_index] != '\n') {
const char c = string[line_index];
if (c == ' ') {
word[word_index+ 1] = '\0';
memcpy(words + index, &word, sizeof(word));
index += 1;
if (word != NULL) {
free(word);
char *word = (char *)calloc(MAX_WORD, sizeof(char));
memset(word, ' ', sizeof(char));
}
++line_index;
word_index = 0;
continue;
}
if (c == '\t')
continue;
if (c == '.')
continue;
if (c == ',')
continue;
word[word_index] = c;
++word_index;
++line_index;
}
index = 0;
if (word != NULL) {
free(word);
}
return *words;
}
Code 2:
char **split(char *string) {
static char *words[MAX_LENGTH / 2];
static int index = 0;
// resetting words
for (int i = 0; i < sizeof(words) / sizeof(words[0]); i++) {
words[i] = NULL;
}
const char *delimiter = " ";
char *ptr = strtok(string, delimiter);
while (ptr != NULL) {
words[index] = ptr;
ptr = strtok(NULL, delimiter);
++index;
}
index = 0;
return words;
}
However I noticed that the memory of word+index is been reassigned to the same location thereby causing word duplication.
strtok() always returns a different pointer into the initial string. This cannot produce duplicates, unless you call it twice with the same input string (maybe with new contents).
However, your function returns a pointer to a static array, which is overwritten on each call to split(), voiding the results of all previous calls. To prevent this,
either allocate new memory in each call (which must be freed by the caller):
char *words = calloc(MAX_LENGTH / 2, 1);
or return a struct instead (which is always copied by value):
struct wordlist { char *word[MAX_LENGTH / 2]; };
wordlist split(char *string)
{
wordlist list = {};
/* ... */
list.word[index] = /* ... */;
/* ... */
return list;
}

how can I append a char to a string allocating memory dynamically in C?

I wrote this code, but inserts garbage in the start of string:
void append(char *s, char c) {
int len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
char c, *s;
int i = 0;
s = malloc(sizeof(char));
while ((c = getchar()) != '\n') {
i++;
s = realloc(s, i * sizeof(char));
append(s, c);
}
printf("\n%s",s);
}
How can I do it?
There are multiple problems in your code:
you iterate until you read a newline ('\n') from the standard input stream. This will cause an endless loop if the end of file occurs before you read a newline, which would happen if you redirect standard input from an empty file.
c should be defined as int so you can test for EOF properly.
s should be null terminated at all times, you must set the first byte to '\0' after malloc() as this function does not initialize the memory it allocates.
i should be initialized to 1 so the first realloc() extends the array by 1 etc. As coded, your array is one byte too short to accommodate the extra character.
you should check for memory allocation failure.
for good style, you should free the allocated memory before exiting the program
main() should return an int, preferably 0 for success.
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
/* append a character to a string, assuming s points to an array with enough space */
void append(char *s, char c) {
size_t len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
int c;
char *s;
size_t i = 1;
s = malloc(i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
*s = '\0';
while ((c = getchar()) != EOF && c != '\n') {
i++;
s = realloc(s, i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
append(s, c);
}
printf("%s\n", s);
free(s);
return 0;
}
when you call strlen it searches for a '\0' char to end the string. You don't have this char inside your string to the behavior of strlen is unpredictable.
Your append function is acually good.
Also, a minor thing, you need to add return 0; to your main function. And i should start from 1 instead if 0.
Here is how it should look:
int main(void){
char *s;
size_t i = 1;
s = malloc (i * sizeof(char));//Just for fun. The i is not needed.
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
s[0] = '\0';
for(char c = getchar(); c != '\n' && c != EOF; c = getchar()) {//it is not needed in this case to store the result as an int.
i++;
s = realloc (s,i * sizeof(char) );
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
append (s,c);
}
printf("%s\n",s);
return 0;
}
Thanks for the comments that helped me improve the code (and for my english). I am not perfect :)
The inner realloc needs to allocate one element more (for the trailing \0) and you have to initialize s[0] = '\0' before starting the loop.
Btw, you can replace your append by strcat() or write it like
size_t i = 0;
s = malloc(1);
/* TODO: check for s != NULL */
while ((c = getchar()) != '\n') {
s[i] = c;
i++;
s = realloc(s, i + 1);
/* TODO: check for s != NULL */
}
s[i] = '\0';

Dynamic memory allocation with char

I'm trying to allocate memory only if i need it for the next while.
char *str = malloc(sizeof(char));
int i = 0;
while(something == true){
str[i] = fgetc(fp);
str = realloc(str, strlen(str)+1);
i++;
}
free(str);
But for some reason the code above give me an "Invalid read of size 1" at strlen().
strlen will not determine the size of the allocated char array even if it contains a null terminated string. See proposed fix although I do not like the code structure overall: You will always end up with an extra allocated character.
char *str = malloc(sizeof(char));
int i = 0;
while(something == true){
str[i] = fgetc(fp);
str = realloc(str, (i+2)*sizeof(char));
i++;
}
// str[i*sizeof(char)]='\0'; <-- Add this if you want a null terminated string
free(str);
I would propose the following code that would avoid allocating the extra character:
char *str = NULL;
int i = 0;
while(something == true){
str = realloc(str, (i+1)*sizeof(char));
str[i] = fgetc(fp);
i++;
}
free(str);
As per documentation, "In case that ptr is a null pointer, the function behaves like malloc, assigning a new block of size bytes and returning a pointer to its beginning."
This is in case you are not reading text and not planning to use such functions as strlen, strcat...
Chunk at a time allocation:
char *str = malloc(sizeof(char));
int i = 0;
const int chunk_size = 100;
while(something == true){
str[i] = fgetc(fp);
if (i % chunk_size == 0)
str = realloc(str, (i+1+chunk_size)*sizeof(char));
i++;
}
// str[i*sizeof(char)]='\0'; <-- Add this if you want a null terminated string
free(str);

Reversing words in a sentence using pointers using c

I'm writing a program in which a function that reverses each word in a string. When I call the function, it will pass the pointer to source string and then return the pointer to modified string.
input: Why always me?
output: yhW syawla ?em
But for some reason, it is not working. No errors. And logic seemed fine to me (i'm not that good with c, btw)
Here's my code:
char *reverse_words(char *source_string)
{
char *startW, *endW;
startW = source_string;
while(*startW != '\0')
{
while(*startW == ' ')
startW++; //Skip multiple spaces in the beginning
endW = startW;
while(*endW != ' ' || *endW != '\0')
endW++;
char *_start = startW;
char *_end = endW - 1;
char temp;
while(_end > _start)
{
temp = *_start;
*_start++ = *_end;
*_end++ = temp;
}
startW = endW;
}
return source_string;
}
void main() {
char *s;
s = malloc(256);
gets(s);
printf("%s\n", s);
char *r = reverse_words(s);
printf("\nReversed String : %s",r);
free(s);
free(r);
}
Also, i'm using codeblocks IDE. After I input my string, it prints it back (scanf and printf in main) and after that, the program stops working.
Any help would be appreciated.
First,
while(*endW != ' ' || *endW != '\0')
is an infinite loop, try this instead:
while(*endW != ' ' && *endW != '\0')
Second,
*_end++ = temp;
should be this:
*_end-- = temp;
In the innermost while(_end > _start) loop you increment both _start and _end. So the condition will never become false. (Well, not until _end overflows.)
I'd recommend figuring out how to do step-by-step debugging in your IDE. Then you can easily understand what exactly goes wrong in a case like this, without simulating the execution in your head.
#include <stdio.h>
#include <stdlib.h>
char *reverse_words(const char *source_string){
const char *startW, *endW;
char *p, *rev = malloc(256);
startW = source_string;
p = rev;
while(*startW != '\0'){
while(*startW == ' ')
*p++ = *startW++;
if(!*startW)
break;
endW = startW;
while(*endW != ' ' && *endW != '\0')
endW++;
const char *endW2 = endW;
do{
*p++ = *--endW;
}while(startW!=endW);
startW = endW2;
}
*p = '\0';
return rev;
}
int main() {
char s[256];
scanf("%255[^\n]", s);
printf("%s,\n", s);
char *r = reverse_words(s);
printf("\nReversed String : %s.", r);
free(r);
return 0;
}

Resources