I'm currently working on making a shell. I want to separate a simple string into a 2D array. At first it was working perfectly, but now I have a strange problem: my simple string "str" changes after I malloc anything. For example, if I create a new array like
char *tmp = malloc(sizeof(char) * 15));
then my str, which was "ls -l", will become something like " OO" or " A".
I've already tried to change the malloc size, but it didn't solve the problem, though it did make str change differently. Her is my code:
char **mem_alloc_2d_array(int nb_rows, int nb_cols) {
char **map = malloc(nb_rows * sizeof(*map + 1));
for (int i = 0; i < nb_rows; i++) {
map[i] = malloc(nb_cols * sizeof(**map + 1));
}
return map;
}
int what_is_x(char const *str) {
int x = 2;
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] == ' ')
x++;
}
return x;
}
char **try_this(char const *str) {
int size = my_strlen(str);
int x = what_is_x(str);
char **words = mem_alloc_2d_array(x, size);
return words;
}
char **my_str_to_wordtab(char *str) {
int j = 0;
int i = 0;
char **words = try_this(str);
if (str[0] == '\n' || str[0] == '\r')
words[0] = NULL;
for (; str[i] == ' ' || str[i] == '\t'; i++);
for (int x = 0; str[i] != '\n'; i++, x++) {
if (str[i] == ' ' || str[i] == '\t') {
words[j][x] = '\0';
j++;
x = 0;
while (str[i] == ' ' || str[i] == '\t')
i++;
}
words[j][x] = str[i];
}
j++;
words[j] = (char *)0;
return words;
}
What I expect is that in the function try_this(), if my str is something big like "ls -l Makefile" then both my_putstr(str) calls will print the same thing, but they don't.
It's unclear what you're talking about with regard to calls to a function my_putstr(str), as neither that function nor any calls to it appear in the code you've presented. Nevertheless, I can say for sure that your memory-allocation code is screwy, and at least partially incorrect. Consider this:
char **map = malloc(nb_rows * sizeof(*map + 1));
. What exactly is the point of the + 1 there? Note that *map has type char *, and therefore so does *map + 1. The sizeof operator computes a result based on the type of its operand, so your sizeof expression computes the same value as sizeof(*map). I'm guessing you probably want
char **map = malloc((nb_rows + 1) * sizeof(*map));
, which reserves space for nb_rows + 1 pointers.
Similarly, this ...
map[i] = malloc(nb_cols * sizeof(**map + 1));
... does not do what you probably intend. To reserve space for a terminator for each string, that would be better written as
map[i] = malloc((nb_cols + 1) * sizeof(**map));
. But since this code is specific to strings, and the size of a char is 1 by definition, I would actually write it like this, myself:
map[i] = malloc(nb_cols + 1);
You not having reserved sufficient space for your data, it is not surprising that you see memory corruption.
Note, too, that checking for memory allocation failure (in which case malloc() returns a null pointer) and handling it appropriately if it occurs are essential for robust code. Do get into the habit of doing that as a matter of routine, although failure to do so is probably not contributing to the particular problem you asked about.
Related
My goal is to create a function that converts a string into an array of "words" resulted from splitting an initial string by a delimiter. All words should be null-terminated.
For example: strtoarr("**hello***world*", "*") should result in {"hello", "world"}. Here's my function.
char **strtoarr(char const *s, char c)
{
char **arr;
size_t i;
size_t j;
size_t k;
arr = malloc(sizeof(**arr) * (strlen(s) + 2));
if (arr == 0)
return (NULL);
i = 0;
k = 0;
while (s[i] != '\0')
{
j = 0;
while (s[i] != c)
{
arr[k][j] = s[i];
if (s[i + 1] == c || s[i + 1] == '\0')
{
j++;
arr[k][j] = '\0';
k++;
}
i++;
j++;
}
j = 0;
while (s[i] == c)
i++;
}
arr[k] = 0;
return (arr);
}
It works only with empty strings and segfaults with everything else. I believe the problem is here.
arr[k][j] = s[i];
But I don't understand what the problem is.
Thanks in advance
There are a number of problems with your code but the most important is the dynamic allocation. Your code does not allocate memory for saving an array of strings (aka an array of array of char).
This line:
arr = malloc(sizeof(**arr) * (strlen(s) + 2));
allocates memory for saving a number chars (i.e. strlen(s) + 2 chars) but that is not what you want. Especially not when arr is a pointer to pointer to char.
A simple approach that you can use is to allocate an array of char pointers and then for each of these pointers allocate an array of char.
This would be:
char** arr = malloc(sizeof(*arr) * NUMBER_OF_WORDS_IN_INPUT);
arr[0] = malloc(NUMBER_OF_CHARACTERS_IN_WORD0 + 1);
arr[1] = malloc(NUMBER_OF_CHARACTERS_IN_WORD1 + 1);
...
arr[NUMBER_OF_WORDS_IN_INPUT - 1] = malloc(NUMBER_OF_CHARACTERS_IN_LAST_WORD + 1);
Then you can store characters into arr using the syntax
arr[i][j] = SOME_CHARACTER;
without segfaults. (It is of cause required that i and j is within bounds of the allocation).
The inner while loop need to end if s[i] is NULL : while (s[i] != c && s[i] != '\0')
You check for s[i + 1] in your if statement but you continue looping.
Also you are allocating way more bytes than necessary, you could have a buffer of same size of your input string, and when the delimiter or NULL is found, you allocate a new row in your array of the needed size and copy the buffer into it.
My problem now is that I have taken space for different words,but I'm having problems storing this as an array. Even though there are some similar posts like this, nothing seems to work for me and I'm completely stuck here. I want to keep this format(i don't want to change the definition of the function). Grateful for all help and comments!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int i, len = 0, counter = 0;
char ** p = 0;
for(i = 0; s[i] != '\0'; i++){
len++;
if(s[i] == ' ' || s[i+1] == '\0'){
counter ++;
for(i = 0; i < len; i++){
p[i] = s[i];
}
}
printf("%d\n", len);
printf("%d\n", counter);
return p;
}
int main() {
char *s = "This is a string";
int n;
int i;
for(i = 0; i < n*; i++){
//also not sure how to print this
}
}
I edited your code and it's now working correctly:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(const char* s, int *n);
char** split(const char* s, int *n) {
int i, len = 0, counter = 0;
char ** p = 0;
for(int i = 0; ; ++i) {
if(s[i] == '\0') {
break;
}
if(s[i] == ' ') {
counter += 1;
}
}
++counter;
p = (char **) malloc(counter * sizeof(char*));
for(int i = 0, c = 0; ; ++i, ++c) {
if(s[i] == '\0') {
break;
}
len = 0;
while(s[len + i + 1] != ' ' && s[len + i + 1] != '\0') {
++len;
}
p[c] = (char *) malloc(len * sizeof(char) + 1);
int k = 0;
for(int j = i; j < i + len + 1; ++j) {
p[c][k++] = s[j];
}
p[c][k] = '\0';
i += len + 1;
}
*n = counter;
return p;
}
int main() {
char *s = "This is a string";
int n;
int i;
char** split_s = split(s, &n);
for(i = 0; i < n; i++) {
printf("%s\n", split_s[i]);
}
}
But I suggest you do a little bit clean-up.
Here is a solution using sscanf. scanf and sscanf considers space as an end of input. I have taken benefit of that to make it work for you.
char *str = (char*) "This is a string";
char buffer[50];
char ** p = (char**)malloc(1 * sizeof(*p));
for (int i = 0; str[0] != NULL; i++)
{
if (i > 0)
{
p = (char**)realloc(p, i * sizeof(p));
}
sscanf(str, "%s", buffer);
int read = strlen(buffer);
str += read + 1;
p[i] = (char*)malloc(sizeof(char)*read + 1);
strcpy(p[i], buffer);
printf("%s\n", p[i]);
}
Since this pointer is growing in both the dimensions, every time a new string is found we need to resize the p itself and then the new address that it contains should be resized too .
My problem now is that I have taken space for different words using malloc, but I'm having problems storing this as an array.
When addressable memory for a collection of strings is needed, then a collection of pointers, as well as memory for each pointer needed.
In your code:
p = (char**)malloc(counter*sizeof(char*));
You have created the collection of pointers, but you have not yet created memory at those locations to accommodate the strings. (By the way, the cast is not necessary)
Here are the essential steps to both create a collection of pointers, and memory for each:
//for illustration, pick sizes for count of strings needed,
//and length of longest string needed.
#define NUM_STRINGS 5
#define STR_LEN 80
char **stringArray = NULL;
stringArray = malloc(NUM_STRINGS*sizeof(char *));// create collection of pointers
if(stringArray)
{
for(int i=0;i<NUM_STRINGS;i++)
{
stringArray[i] = malloc(STR_LEN + 1);//create memory for each string
if(!stringArray[i]) //+1 room for nul terminator
{
//handle error
}
}
}
As a function it could look like this: (replacing malloc with calloc for initialized space)
char ** Create2DStr(size_t numStrings, size_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
using this in your split() function:
char** split(const char* s, int *n){
int i, len = 0, counter = 0, lenLongest = 0
char ** p = 0;
//code to count words and longest word
p = Create2DStr(counter, longest + 1); //+1 for nul termination
if(p)
{
//your searching code
//...
// when finished, free memory
Let's start at the logic.
How does a string like A quick brown fox. get processed? I would suggest:
Count the number of words, and the amount of memory needed to store the words. (In C, each string ends with a terminating nul byte, \0.)
Allocate enough memory for the pointers and the words.
Copy each word from the source string.
We have a string as an input, and we want an array of strings as output. The simplest option is
char **split_words(const char *source);
where the return value is NULL if an error occurs, or an array of pointers terminated by a NULL pointer otherwise. All of it is dynamically allocated at once, so calling free() on the return value will free both the pointers and their contents.
Let's start implementing the logic according to the bullet points above.
#include <stdlib.h>
char **split_words(const char *source)
{
size_t num_chars = 0;
size_t num_words = 0;
size_t w = 0;
const char *src;
char **word, *data;
/* Sanity check. */
if (!source)
return NULL; /* split_words(NULL) will return NULL. */
/* Count the number of words in source (num_words),
and the number of chars needed to store
a copy of each word (num_chars). */
src = source;
while (1) {
/* Skip any leading whitespace (not just spaces). */
while (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' ')
src++;
/* No more words? */
if (*src == '\0')
break;
/* We have one more word. Account for the pointer itself,
and the string-terminating nul char. */
num_words++;
num_chars++;
/* Count and skip the characters in this word. */
while (*src != '\0' && *src != '\t' && *src != '\n' &&
*src != '\v' && *src != '\f' && *src != '\r' &&
*src != ' ') {
src++;
num_chars++;
}
}
/* If the string has no words in it, return NULL. */
if (num_chars < 1)
return NULL;
/* Allocate memory for both the pointers and the data.
One extra pointer is needed for the array-terminating
NULL pointer. */
word = malloc((num_words + 1) * sizeof (char *) + num_chars);
if (!word)
return NULL; /* Not enough memory. */
/* Since 'word' is the return value, and we use
num_words + 1 pointers in it, the rest of the memory
we allocated we use for the string contents. */
data = (char *)(word + num_words + 1);
/* Now we must repeat the first loop, exactly,
but also copy the data as we do so. */
src = source;
while (1) {
/* Skip any leading whitespace (not just spaces). */
while (*src == '\t' || *src == '\n' || *src == '\v' ||
*src == '\f' || *src == '\r' || *src == ' ')
src++;
/* No more words? */
if (*src == '\0')
break;
/* We have one more word. Assign the pointer. */
word[w] = data;
w++;
/* Count and skip the characters in this word. */
while (*src != '\0' && *src != '\t' && *src != '\n' &&
*src != '\v' && *src != '\f' && *src != '\r' &&
*src != ' ') {
*(data++) = *(src++);
}
/* Terminate this word. */
*(data++) = '\0';
}
/* Terminate the word array. */
word[w] = NULL;
/* All done! */
return word;
}
We can test the above with a small test main():
#include <stdio.h>
int main(int argc, char *argv[])
{
char **all;
size_t i;
all = split_words(" foo Bar. BAZ!\tWoohoo\n More");
if (!all) {
fprintf(stderr, "split_words() failed.\n");
exit(EXIT_FAILURE);
}
for (i = 0; all[i] != NULL; i++)
printf("all[%zu] = \"%s\"\n", i, all[i]);
free(all);
return EXIT_SUCCESS;
}
If we compile and run the above, we get
all[0] = "foo"
all[1] = "Bar."
all[2] = "BAZ!"
all[3] = "Woohoo"
all[4] = "More"
The downside of this approach (of using one malloc() call to allocate memory for both the pointers and the data), is that we cannot easily grow the array; we can really just treat it as one big clump.
A better approach, especially if we intend to add new words dynamically, is to use a structure:
typedef struct {
size_t max_words; /* Number of pointers allocated */
size_t num_words; /* Number of words in array */
char **word; /* Array of pointers */
} wordarray;
Unfortunately, this time we need to allocate each word separately. However, if we use a structure to describe each word in a common allocation buffer, say
typedef struct {
size_t offset;
size_t length;
} wordref;
typedef struct {
size_t max_words;
size_t num_words;
wordref *word;
size_t max_data;
size_t num_data;
char *data;
} wordarray;
#define WORDARRAY_INIT { 0, 0, NULL, 0, 0, NULL }
static inline const char *wordarray_word_ptr(wordarray *wa, size_t i)
{
if (wa && i < wa->num_words)
return wa->data + wa->word[i].offset;
else
return "";
}
static inline size_t wordarray_word_len(wordarray *wa, size_t i)
{
if (wa && i < wa->num_words)
return wa->word[i].length;
else
return 0;
}
The idea is that if you declare
wordarray words = WORDARRAY_INIT;
you can use wordarray_word_ptr(&words, i) to get a pointer to the ith word, or a pointer to an empty string if ith word does not exist yet, and wordarray_word_len(&words, i) to get the length of that word (much faster than calling strlen(wordarray_word_ptr(&words, i))).
The underlying reason why we cannot use char * here, is that realloc()ing the data area (where the word pointers would point to) may change its address. If that were to happen, we'd have to adjust every pointer in our array. It is much easier to use offsets to the data area instead.
The only downside to this approach is that deleting words does not mean a corresponding shrinkage in the data area. However, it is possible to write a simple "compactor" function, that repacks the data to a new area, so that holes left by deleted words are "moved" to the end of the data area. Usually, this is not necessary, but you might wish to add a member to the wordarray structure, say the number of lost characters from word deletions, so that the compaction can be done heuristically the next time the data area would be otherwise resized.
I've written a function called safecat that adds one string (called ct) to the end of another string (called s).
The resulting new s is then returned.
In the main function my task is to check if my function worked the intended way, and if so, then print the result of my function safecat.
The problem I have is that when I assign the return value of safecat to another char-string (in this case str) in my main function, the stuff in str which comes from ct is just garbage.
I don't understand where the problem is, if I just do printf("%s", safecat(s, ct)); I get the correct result.
Here you see my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *safecat(char *s, const char *ct);
int main()
{
char s[] = "Twin";
const char ct[] = "Peaks";
char *str = safecat(s, ct);
if(str == NULL){
printf("Error in function!");
return 1;
}
printf("\n%s\n", str);
return 0;
}
char *safecat(char *s, const char *ct){
int i, k, j = 0;
int max_count = strlen(s) + strlen(ct) + 1;
for(i = strlen(s); i < max_count; i = i + sizeof(char)){
*(s + i) = (char *) malloc((strlen(s) + strlen(ct) + 1) * sizeof(char));
if(!(s + i)){
for(k = strlen(s) / sizeof(char); k < i; k++){
free(*(s + k));
}
return NULL;
}
*(s + i) = *(ct + j);
j++;
}
return s;
}
I think the error happens when I assign safecat to str.
When I print out str I get "TwinP' a" instead of "TwinPeaks".
Thanks for helping!
You can not change the size of the array
char s[] = "Twin";
in the function using malloc.
And in any case this loop
for(i = strlen(s); i < max_count; i = i + sizeof(char)){
*(s + i) = (char *) malloc((strlen(s) + strlen(ct) + 1) * sizeof(char));
if(!(s + i)){
for(k = strlen(s) / sizeof(char); k < i; k++){
free(*(s + k));
}
return NULL;
}
*(s + i) = *(ct + j);
j++;
}
does not make sense. For example the expression *(s + i) has type char instead of the type char *. And also it is not clear why a memory is allocated in each iteration of the loop.
A correct approach is to allocate dynamically a new array with the size equal to the sum of the sizes of the source arrays plus one and to copy the source arrays in the allocated array.
Here is a demonstrative program that shows how it can be done. Also you should free the allocated memory when the array is not needed any more.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char * safecat(const char *s1, const char *s2)
{
char *result = malloc(strlen(s1) + strlen(s2) + 1);
if (result != NULL)
{
char *p = result;
while (*s1) *p++ = *s1++;
do { *p++ = *s2; } while (*s2++);
}
return result;
}
int main( void )
{
char s[] = "Twin";
char ct[] = "Peaks";
char *str = safecat(s, ct);
if (str == NULL)
{
puts("Error in function!");
return 1;
}
puts(str);
free(str);
return 0;
}
The program output is
TwinPeaks
Of course you could use standard string functions strcpy and strcat instead of the loops that can be both written even in the return statement
return result == NULL ? result : strcat( strcpy( result, s1 ), s2 );
I think that you misunderstand the meaning of malloc.
You are using malloc as if it would make a new slot in or at the end of a string. malloc(sizeX), however, reserves a new memory block with sizeX bytes somewhere in the memory (rather than at a particular position that you could determine). And the result of malloc is a pointer to a memory block, not a character.
So what you do with expression *(s + i) = (char *) malloc((strlen(s) + strlen(ct) + 1) * sizeof(char)); is actually writing a pointer value (usually something obscure when viewed as characters) as value directly into string s at the position of s's string terminating character;
For your safecat, first reserve memory for the new concatenated result string, then fill it up with your logic (I suppose this is a programming assignment and you are not allowed to use strcpy, right?
int slen = strlen(s);
int ctlen = strlen(ct);
char *result = (char *) malloc((slen + ctlen + 1) * sizeof(char));
for (int i=0; i<slen; i++)
result[i] = s[i];
for (int i=0; i < ctlen + 1 ; i++) // note "i < ctlen + 1" for considering string terminator of ct.
result[i+slen] = ct[i];
When you copy the new string to the old string you wind up putting in the null character after the initial P. What you need to do is as follows.
malloc a new buffer (ns) of the size of maxcount.
strcpy s into ns
strcpy ct into ns + strlen(s)
Note. If you are not allowed to used strcpy(), then write your own version as a function safecpy() in the a similar fashion to safecat()
return ns
Note that you would want to free ns sometime later in the program if you no longer need it.
Thank you to everyone who helped. It was a coding assignment and I was able to figure it out the next day.
The stuff I've written must have seemed to be very chaotic as I've just learned about pointers, and yes, I was not allowed to use 'strcat()' or 'strcpy()'.
I tried to make a function which replace every word in a text with the word shifted to right by 'k' times.
the code look like this:
void operation_3(char *string, int k){
int len = 0, i;
int string_len = strlen(string);
char *word;
char s[12] = " .,?!\"'";
char *dup;
dup = strdup(string);
word = strtok(dup, s);
while (word != NULL) {
len = strlen(word);
char *new_word = (char *)malloc(len * sizeof(char));
for (i = 0; i < k; i++) {
new_word = shift_to_right(word);
}
string = replace_word(string, word, new_word);
word = strtok(NULL, s);
}
}
shift_to_right is:
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
int i;
for (i = len - 1; i >= 0; i--) {
temp = string[i+1];
string[i+1] = string[i];
string[i] = temp;
}
return string;
}
replace_word is:
char *replace_word(char *string, char *word, char *new_word) {
int len = strlen(string) + 1;
char *temp = malloc(len * sizeof(char));
int temp_len = 0;
char *found;
while (found = strstr(string, word)) {
if (strlen(found) != strlen(word) || isDelimitator(*(found - 1)) == 1) {
break;
}
memcpy(temp + temp_len, string, found - string);
temp_len = temp_len + found - string;
string = found + strlen(word)
len = len - strlen(word) + strlen(new_word);
temp = realloc(temp, len * sizeof(char));
memcpy(temp + temp_len, new_word, strlen(new_word));
temp_len = temp_len + strlen(new_word);
}
strcpy(temp + temp_len, string);
return temp;
}
and isDelimitator is:
int isDelimitator(char c) {
if(c == ' ' || c == '.' || c == ',' || c == '?' || c == '!' ||
c == '"' || c == '\0' || c == '\'') {
return 0;
}
else return 1;
}
I tested shift_to_right, replace_word and isDelimitator and work fine. But the final function, operation_3 doesn't work as expected. For example, for input: "Hi I am John" and for k = 1 the output is : "Hi I am John". Basically operation_3 doesn't modify the string. Any advice, corrections please?
There are a few things which I see are possibly the reason for error.
1) In operation_3 you do this : new_word = shift_to_right(word); And, in the definition of char *shift_to_right(char *string) you modify the string itself and return a pointer to it. So, if you called shift_to_right(word) and word = "Hi" then after the execution of shift_to_right both word and new_word are now pointing to the same string "iH", so in replace_word when you pass both the words and check for the substring word you will always get NULL, because, there is no substring "iH".
A possible solution, in shift_to_right add a statement,
char *new_string = strdup(string);
and instead of swapping the characters in string, swap the characters now in new_string and return the new_string from the function.
Your code shall look like this ::
char *shift_to_right(char *string){
char temp;
int len = strlen(string) - 1;
char *new_string = strdup(string);
int i;
for (i = len - 1; i >= 0; i--) {
temp = new_string[i+1];
new_string[i+1] = new_string[i];
new_string[i] = temp;
}
return new_string;
}
2) In the function replace_word, for a moment let us consider that the above mentioned error does not occur and replace_word get called with the parameters :: replace_word(string, "Hi", "iH");.
So, when you perform found = strstr(string, word), it gives you a pointer to the first letter where Hi started. So, in this case, if your string was "Hi I am John", then you get a pointer to the first H, and when you perform strlen(found) you will get 12(length of string left starting from the pointer) as the output, and strlen(word) will always be less (unless found points to the last word in the string), so in most cases your if condition becomes true and you break from the loop, without any swapping.
Moreover, as you yourself pointed out in the comments that strstr will return Johns as well if you want a substring John the only solution for this would be to run a loop and check that in string after John if there is delimiter character or not, if there is no delimiter character, then this is not the substring that you needed.
replace_word shall look something like this ::
void replace_word(char *string, char *word, char *new_word) {
char *found = strstr(string, word);
int len = strlen(word);
while(found) {
char temp = *(found + len);
if(isDelimeter(temp) == 0) {
break;
} else {
found = strstr(found + len + 1);
}
}
if(found != NULL) {
for(int i = 0; i < len; i++) {
*(found + i) = new_word[i]; // *(found + i) is accessing the i^th, character in string from the pointer found
}
}
}
I think this replace_word shall work, you can directly modify the string, and there is no need to actually make a temp string and return it. This reduces the need of allocating new memory and saving that pointer.
I hope this could help!
EDIT :: Since we have been using strdup in the code, which dynamically allocates memory of the size of the string with an extra block for the \0 character, we shall take care of freeing it explicitly, so it will be a good idea according to me free the allocated memory in replace_word just before we exit the function since the new_word is useless after it.
Moreover, I saw a statement in your code::
1) char *new_word = (char *)malloc(len * sizeof(char));
Just before you start the shifting the words, I hope you understand that you do not need to do it. new_word is just a pointer, and since we now allocated memory to it in strdup we do not need to do it. Even before, considering the code that you had written there was no reason to allocate memory to new_word since you were returning the address of the array, which was already in the stack, and would stay in the stack till the end of the execution of the program.
This code is simpler than what you have, and it prints all the word delimiters that were in the input string. And rather than looking for specific punctuation characters, it checks alphanumeric instead.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(void)
{
char instr[] = "Hi! I am 'John' ;)";
int lennin = strlen(instr);
int shifts, i, len, index, start, next;
printf("Working with %s\n", instr);
for(shifts=0; shifts<5; shifts++) { // various examples
printf("Shifts = %d ", shifts);
start = 0;
while(start < lennin) {
while (start < lennin && !isalnum(instr[start])) { // find next alphanum
printf("%c", instr[start]); // output non-alphanum
start++;
}
next = start + 1;
while (isalnum(instr[next])) // find next non-alphanum
next++;
len = next - start;
for(i=0; i<len; i++) { // shift the substring
index = i - shifts;
while(index < 0) index += len; // get index in range
printf("%c", instr[start + (index % len)]); // ditto
}
start = next; // next substring
}
printf("\n");
}
return 0;
}
Program output:
Working with Hi! I am 'John' ;)
Shifts = 0 Hi! I am 'John' ;)
Shifts = 1 iH! I ma 'nJoh' ;)
Shifts = 2 Hi! I am 'hnJo' ;)
Shifts = 3 iH! I ma 'ohnJ' ;)
Shifts = 4 Hi! I am 'John' ;)
I have written a program to take input and dynamically allocate memory using realloc(), however there seems to be an error because if I print the final string char by char I seem to have 2 empty bytes at the end, I am sure this is going to be something silly but I have spent some time trying to discover the cause and have failed so hope to learn something here.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int count = 0;
char *str;
char tmp;
str = malloc(sizeof(char));
while (tmp != '\n') {
tmp = getchar();
str = realloc(str, (count + 1) * sizeof(char));
*(str + count) = tmp;
count += 1;
}
*(str + count) = '\0';
puts(str);
// This is just to try and see what was happening
for (int i = 0; i <= count; i++)
printf("str[%d] = %c\n", i, str[i]);
free(str);
str = NULL;
return EXIT_SUCCESS;
}
Four things to mention here.
while(tmp != '\n') is reading uninitialised automatic local variable value without initialization. It invokes undefined behaviour.
str = realloc(str, (count + 0x01) * sizeof(char)); is very bad, if realloc() fails, you'll lose the actual pointer, too. Always use a temporary pointer to hold the return value from realloc() and after proper error check, assign it back to the main pointer.
sizeof(char) is guaranteed to be 1. You don't need to use as a multiplier. It's redundant.
The for loop condition, should be i < count otherwise, you'll run into off-by-one error. C uses 0 based indexing.
That said,
You should always check for the success of the return vale of realloc() and family of functions for success before using the returned pointer.
getchar() returns an int. You should change the type of tmp to int tmp = 0;
This loop should look at least like
for(int i = 0; i < count; i++)
^^^
printf("str[%d] = %c\n", i, str[i]);
Or it would be better to write
for(int i = 0; str[i]; i++)
printf("str[%d] = %c\n", i, str[i]);
Or
int i = 0;
for ( char *p = str; *p; ++p )
printf( "str[%d] = %c\n", i++, *p );
And change these statements
while(tmp != '\n') {
tmp = getchar();
to
while ( ( tmp = getchar() ) != EOF && tmp != '\n' )
Also it would be more safe instead of this statement
str = realloc(str, (count + 0x01) * sizeof(char));
to write
char *p = realloc(str, (count + 0x01) * sizeof(char));
if ( !p ) break;
else str = p;
Apart from the uninitialized variable access, the two “empty characters” are:
a newline, since you check for \n before you read and store the next character, and
a character in uninitialized memory since you’re incorrectly looping while i <= count and not while i < count.
Use a for (;;) (infinite loop) and check if (tmp == '\n') { break; } immediately after getchar() to avoid both the uninitialized variable access and trailing newline.