I'm trying to take a string and break it into "word" components and store that in an array of strings.
"Hello my name is Bill." should give back a char** with elements, "Hello", "my", "name", "is", and "Bill."
My code will compile however I keep encountering a runtime error (I don't get warnings anymore and my debugger gdb doesn't work)>
I'm running on minGW on Window 8.
#include <stdio.h>
#include <stdlib.h>
char** words(char* string)
{
int i = 0;
int j = 0;
int k =0;
int count = 0;
char** stringArray = (char**) malloc(sizeof(char)*30*30);
while( string[i] != '\0' )
{
if(string[i] != ' ')
{
j =0;
while(string[i+j+1] != ' ')
{
j++;
}
i = i+j;
for(k=0; k<=j; k++)
{
stringArray[count][k] = string[i+k];
}
count++;
}
i++;
}
return stringArray;
}
int main()
{
char message[20] = "abcd efgh ijkl mno";
char** wordArray = words(message);
printf("%c\n\n", wordArray[0][0]);
int i =0;
while(wordArray[i])
{
printf("%s\n", wordArray[i]);
i++;
}
printf("\nThe problem is not with the words function");
return 0;
}
There are couple of issues that have been mentioned in the comments.
The allocation should look something like:
#include <ctype.h> // for isspace()
#define MAXSTRLEN 30 // using a symbolic constant
char **stringArray;
int i, j, k;
stringArray = malloc(sizeof(char*) * MAXSTRLEN); // don't cast from malloc
for (i = 0; i < 30; ++i) {
stringArray[i] = malloc(sizeof(char) * MAXSTRLEN);
}
// TODO error checking: malloc could return NULL
while copying the substrings would look like:
i = 0;
j = 0;
while( string[i] != '\0') // go through the whole string
{
while (string[i] != '\0' && isspace(string[i])) {
i++; // skip whitespaces
}
k = 0;
while (string[i] != '\0' && !isspace(string[i])) { // copy word until whitepace or end of string
stringArray[j][k++] = string[i++];
}
stringArray[j][k] = '\0'; // EOS !!!
j++;
}
and printing (j is number of words actually read):
for (i = 0; i < j/*30*/; ++i) { // (!) how to print
printf("%s\n", stringArray[i]);
}
And, yes strtok would also do the job.
In words() you're assigning values to stringArray as a two-dimensional array, and in main() you're reading values from it as an array of pointers. Those are not the same thing.
So you need to change it so that you're consistently treating it as a 2D array, or so that you're consistently treating it as an array of pointers (char* to be exact). Either will work... see the comments above for elaboration.
This code is all wrong.
char** stringArray = (char**) malloc(sizeof(char)*30*30);
First of all, sizeof(char) is always one, second, you don't need to cast a void. So:
char **stringArray = malloc(30 * 30);
But that doesn't make any sense because it's an array of char *, so you should allocate in terms of that:
char **stringArray = malloc(sizeof(char *) * 30);
Or even better:
char **stringArray = malloc(sizeof(*stringArray) * 30);
So now you have an array with 30 char *, but each of those is not initialized, so you need to do that:
for (i = 0; i < 30; i++)
stringArray[i] = malloc(sizeof(**stringArray) * 30);
If you don't do that, you can't access stringArray[count][k].
And then you assume the last element in the array is NULL, but you never set it, so you either do stringArray[count] = NULL at the end of words(), or you do calloc() instead of malloc().
I'm not analyzing the code beyond that; it's just all wrong.
Related
Full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void printarray(int* array, int arraysize){
for (int i = 0; i<arraysize; i++){
printf("%d\n", *array);
array++;
}
}
void printStrArray(char** array, int arraysize){
int j = 0;
for (int i = 0; i<arraysize; i++){
j = 0;
while (array[i][j] != '\0'){
printf("%c", array[i][j]);
j++;
}
printf("\n");
}
}
int isStringInArray(char* string, char** stringArray, int arrayLen){ // returns 1 if string is contained in the array.
for (int i = 0; i < arrayLen; i++){
if (strcmp(string, stringArray[i]) == 0){
//printf("%s is equal to %s %d\n", string, stringArray[i], i);
return 1;
}
}
return 0;
}
int lenstring(char* string){ // checks string length (works only if string has null character at the end.)
char currchar = string[0];
int strlen = 0;
while (currchar != '\0'){
strlen++;
currchar = string[strlen];
}
return strlen;
}
char** riassemble(char* stringa){
char** riassembleds = calloc(1, sizeof(char*));
char* charLen = malloc(sizeof(char));
riassembleds[0] = charLen;
int riassembledLen = 1;
int stringalen = lenstring(stringa);
char tempstring[stringalen];
strcpy(tempstring, stringa);
for (int i = 0; i < stringalen; i++){
for (int j = 0; j < stringalen; j++){
tempstring[i] = stringa[j];
tempstring[j] = stringa[i];
//printf("%s\n", tempstring);
if (isStringInArray(tempstring, riassembleds, riassembledLen) == 0){
riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*));
riassembledLen++;
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
printf("%p\n", riassembleds[riassembledLen-1]);
strcpy(riassembleds[riassembledLen-1], tempstring);
}
strcpy(tempstring, stringa);
}
}
*charLen = (char)riassembledLen;
riassembleds[0] = charLen; /*return the array with the length of the it casted into a char pointer as the first element*/
return riassembleds;
}
int main(int argc, char *argv[]){
char** array = riassemble("ciao");
int arraylen = (int)(*(array[0]));
printf("\n%d\n", arraylen);
printStrArray(array, arraylen);
for (int i=0; i<arraylen; i++) {
free(array[i]);
}
free(array);
return 0;
}
i'm making a function that returns an array of pointers to a char, in which the first element is a pointer that points to the length of the array casted into a char.
When i try to free the elements in the array with
char** array = riassemble("ciao"); /*Creates the array*/
int arraylen = (int)(*(array[0])); /*Gets the length*/
for (int i=0; i<arraylen; i++) {
free(array[i]);
}
The program crashes after trying to free the second element of the array which is defined here:
riassembleds = realloc(riassembleds, (riassembledLen+1)*sizeof(char*));
riassembledLen++;
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
printf("%p\n", riassembleds[riassembledLen-1]);
strcpy(riassembleds[riassembledLen-1], tempstring);
I really don't understand why this happens, one thing i noticed is that if i print the pointers that it's trying to free, they're not the same as when i print them right after allocating them in the riassemble function, but other than that i have no idea what i'm doing wrong.
edit: i was wrong about the fact that they're not the same, they actually are, i got confused.
Your function lenstring will return the length of the string without the terminating null character.
Therefore, the line
char tempstring[stringalen];
will create an array tempstringthat is sufficient in size to store the string stringa without the terminating null character.
However, the function call
strcpy(tempstring, stringa);
requires that tempstring is large enough to store stringa with the terminating null character. You are therefore writing to tempstring out of bounds, invoking undefined behavior.
In order to fix this, I recommend that you change the line
char tempstring[stringalen];
to:
char tempstring[stringalen+1];
The line
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
has the same problem, as it only allocates sufficient space without the terminating null character, which causes the line
strcpy(riassembleds[riassembledLen-1], tempstring);
to invoke undefined behavior for the same reason.
Therefore, the line
riassembleds[riassembledLen-1] = calloc(stringalen, sizeof(char));
should be changed to
riassembleds[riassembledLen-1] = calloc(stringalen+1, sizeof(char));
I was able to find these errors very easily by using AddressSanitizer on your program.
I'm trying to remove consecutive repeated characters from a given string.
Example:
bssdffFdcrrrtttii ***#
output is supposed to be:
bsdfFdcrti *#
This code doesn't work and only prints the first char (b), I want to learn about my mistake.
when I'm doing a printf test, it works but not for spaces.
I think the problem might be with the new char array.
void Ex6() {
char* string[80];
scanf("%s", &string);
puts(removeDup(string));
}
char* removeDup(char *string) {
int i, c = 0;
char* newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
return newString;
}
There are several problems with your program:
The declaration of newString should be char newString[80], i.e., an array of characters and not an array of pointers-to-characters, and likewise for the declaration in Ex6.
The call to scanf should then be scanf("%s", string), since string is already the address of an array of characters, but...
Use fgets to read a string from the user to ensure that you read whitespace, if it's important, and that the buffer is not exceeded.
newString is allocated on the stack and so should not be returned to the caller. It is better to do a char *newString = strdup(string), or, slightly less sloppy, char *newString = malloc(strlen(string)+1), which will call malloc for a block of memory sufficient to hold the original string, and thus the version without duplicates -- the comments rightly point out that this could be optimized. In principle, the caller, i.e., Ex6, must free the returned pointer to avoid a memory leak but it hardly matters in such a short program.
The result needs a null terminator: newString[c] = '\0'.
Otherwise, the removeDup function seems to work correctly.
So, putting all of that together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* removeDup(const char *string)
{
size_t i, c = 0;
size_t string_len = strlen(string);
char *newString = malloc(string_len + 1);
for (i = 0; i < string_len; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}
#define MAX_STRING_LEN 80
void Ex6() {
char string[MAX_STRING_LEN];
char* result;
if (fgets(string, MAX_STRING_LEN, stdin) != NULL) {
result = removeDup(string);
printf("%s", result);
free(result);
}
}
Finally, I agree with #tadman's comment. Since the input string must anyway be traversed to calculate the length, we may as well optimize the size of the result string:
char* removeDup(const char *string)
{
size_t i, c = 0;
char *newString;
for (i = 0; string[i] != '\0'; i++)
c += (string[i] != string[i + 1]);
newString = malloc(c + 1);
for (i = c = 0; string[i] != '\0'; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}
There are quite a few issues in your program. It wouldn't even compile let alone run. Also, the most problematic issue is that you are returning a pointer to a local variable from a function that ceases its scope upon completion. A simplified version of your program is as follows:
void Ex6()
{
char string[80];
scanf("%s", string);
int i, c = 0;
char newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
puts(newString);
}
You can do it with O(n) time and O(1) space, by modifying existing string:
#include <stdio.h>
char* removeDup(char* input) {
char* newTail = input, *oldTail = input;
while (*oldTail) {
if (*newTail == *oldTail) {
++oldTail;
} else {
*++newTail = *oldTail++;
}
}
return newTail;
}
int main() {
char string[] = "bssdffFdcrrrtttii ***#";
char* newEnd = removeDup(string);
char* tmp = string;
while (tmp != newEnd) {
printf("%c", *tmp++);
}
//Print the last char if string had any duplicates
if(*tmp) {
printf("%c", *tmp++);
}
return 0;
}
I have two strings:
char *str1 = "this is a test";
char *str2 = "ts bd a";
I'm trying to write a function that returns a new string with the same chars from the two string without duplicates (also ' ' is duplicate). eg.:
char *retStr = GetSameChars(str1, str2); //returns "ts a";
How can I do that?
What I'm tried:
char *GetSameChars(char str1[], char str2[]) {
int found = -1, i , j = 0, biggest, index = 0;
char *retArr, *star = '*';
int str1Len, str2Len, count = 0;
str1Len = strlen(str1);
str2Len = strlen(str2);
biggest = str1Len > str2Len ? str1Len : str2Len;
retArr = (char *)malloc(sizeof(char) * count);
for (i = 0; i < str1Len; i++) {
for (j = 0; j < str2Len; j++) {
if (str1[i] == str2[j] && found == -1) {
count++;
found = j;
} else
if (str2[j] == str2[found])
str2[j] = star; //Throw an exception
}
found = -1;
}
retArr = (char *)malloc(sizeof(char) * count);
j = 0;
for (i = 0; i < str2Len; i++)
if (str2[i] != '*')
retArr[j++] = str2[i];
for (i = 0; i < str2Len; i++)
printf("%c", retArr[i]);
}
When I tried the line str2[j] = star; I got an exception.
What is my mistake?
My recommendations would be: keep it simple; get to know the C standard library; write less, test more.
Some specific problems with your code: you pass the wrong variable to malloc(); you estimate the answer to fit in the size of the larger of the two strings but it will actually fit into the smaller of the two; you modify an argument string str2[j] = star -- you should be treating the arguments as readonly; you malloc() retArr twice unnecessarily, leaking the first one when you allocate the second; your algorithm simply doesn't work.
Although a lookup table, as others have suggested, would be more efficient, let's use the standard library routine strchr() to solve this problem:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *getSameChars(const char *string1, const char *string2) {
size_t string1_length = strlen(string1);
size_t string2_length = strlen(string2);
size_t shortest = string1_length < string2_length ? string1_length : string2_length;
char *common_pointer, *common = malloc(shortest + 1);
*(common_pointer = common) = '\0';
for (size_t i = 0; i < string1_length; i++) {
// character found in both input strings, but not yet in common string
if (strchr(string2, string1[i]) != NULL && strchr(common, string1[i]) == NULL) {
*common_pointer++ = string1[i];
*common_pointer = '\0';
}
}
return common;
}
int main() {
char *stringA = "this is a test";
char *stringB = "ts bd a";
char *result = getSameChars(stringA, stringB);
printf("%s\n", result);
free(result);
return(0);
}
Your code complains because you are trying to assign a pointer to a char, to get the value inside a pointer you need to use the * operator like so:
*star;
a good way to check if a letter have already appeared(if you want to use it on all of the ascii table then 128) is to use a lookup table. first you will need to declare an array the length of all letters in the alphabet like so:
char lut[26];
If it is a global variable then it will be set to 0, then all you need to do is go to the index of the char you got and mark it as 1, a simple if will later be able to determine if a letter has already appeard.
example:
lut[toupper(somechar) - 'A'] = 1;
In this example you set the char in the lookup table that is equivalent to the somechar variable as 1, marking it has already appeared.
hope this helps.
I am trying to write a program which merges a lines from stdin and print only those sentences which are longer than 80 characters. The first found line works well - the later ones, however, are empty. I think that I am doing something wrong with the line
current_sentence = malloc(sentence_len);.
How can I reassign a string correctly?
Code
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define BUFFERSIZE 100
char* merge_string(char *text[], int n){
int i;
char *result = malloc(BUFFERSIZE * n);
for (i=0; i < n; i++){
strcat(result, text[i]);
}
return result;
}
int main(int argc, char *argv[]){
char buffer[BUFFERSIZE];
int i = 0;
char *text[BUFFERSIZE];
while(fgets(buffer, BUFFERSIZE, stdin) != NULL){
text[i] = strdup(buffer);
i++;
}
char *sentence = merge_string(text, i);
int sentence_len = strlen(sentence);
int j = 0;
int counter = 0;
char *current_sentence = malloc(sentence_len);
while (j < sentence_len){
current_sentence[counter] = sentence[j];
if (sentence[j] == '\n' && counter >= 80){
printf(":::HIT:::%s\n\n\n", current_sentence);
counter = 0;
current_sentence = malloc(sentence_len);
}
else if (sentence[j] == '\n'){
puts("Resetting counter");
counter = 0;
}
j++; counter++;
}
return 0;
}
Output
make 1_17; ./1_17 < example.txt
make: `1_17' is up to date.
Resetting counter
Resetting counter
:::HIT:::SHenri Cartier-Bresson (1908-2004) said "Your first 10,000 photographs are your worst," but he shot more than one an hour.)
Resetting counter
:::HIT:::
Resetting counter
:::HIT:::
You are not terminating current_sentence with a null character ('\0'). If you want printf to print the string properly, better make sure it is null-terminated.
By the way, there's no need for a second malloc. Reuse the memory allocated for current_sentence without re-allocating.
Also note that you're not freeing the allocated memory properly. You should be use a matching free call for each malloc. Perhaps this isn't a problem now, but it creates a memory leak.
Your loop should look something like this:
while (j < sentence_len)
{
current_sentence[counter] = sentence[j];
if (sentence[j] == '\n')
{
if (counter >= 80)
{
current_sentence[counter + 1] = '\0'; // Make string null-terminated
printf(":::HIT:::%s\n\n\n", current_sentence);
}
else
{
puts("Resetting counter");
}
counter = 0;
}
else
{
counter++;
}
j++;
}
free(current_sentence); // Free allocated memory
Then again, as mentioned in a comment, you'd rather let fgets do the work for you indeed.
char *text[BUFFERSIZE];
should be
char text[BUFFERSIZE];
For some reason, I can't get this to work! Can anyone tell me where I've gone wrong? This is supposed to reverse the words in a give string (i.e from "this is a test" to "test a is this")
#include <stdio.h>
#include <stdlib.h>
char *reverse(char const *input)
{
char *ret = (char *)malloc(sizeof(char) * strlen(input));
int length = 0;
int numWords = 1;
int i;
for(i=0; input[i]!=NULL; i++)
{
length++;
if(input[i]==' ')
numWords++;
}
char words[numWords];
int currentWord = numWords;
for(i=0; input[i]!=NULL; i++)
{
if (input[i]==' '){
currentWord--;
}else{
words[currentWord] = words[currentWord] + input[i];
}
}
for(i=0; i < numWords; i++)
{
ret = ret + words[i];
}
return ret;
}
int main(int argc, char **argv)
{
int nTestcase = 0;
int i = 0;
char inputstr[100];
char *reversedStr = NULL;
scanf("%d\n", &nTestcase);
for (i = 0; i < nTestcase; i++)
{
fgets(inputstr, 100, stdin);
reversedStr = reverse(inputstr);
printf("%s\n", reversedStr);
free(reversedStr);
memset(inputstr, 0, 100);
}
return 0;
}
words[currentWord] = words[currentWord] + input[i];
You can't add characters to each other like that expecting string concatenation. And I imagine you expect words to be an array of words (i.e. strings), but its type is not that, words is just an array of characters.
Like #Tom said, you're doing this again in the last for loop:
ret = ret + words[i];
Joseph, unlike other programming languages ( c# or PHP ), string handling functions of C are quite basic.
You cannot add strings directly, however there are a host of library functions you can use to accomplish the same task.
Check out,
strtok - Use it to split the string to words. strtok reference
strncat - Use to concatenate strings
strings in C are just byte arrays terminated with the null character ( byte with value 0).
Here is shorter/cleaner way
private char[] reverseWords(char[] words) {
int j = words.length - 1;
for (int i = 0; i < (words.length)/2; i++) {
if(i==j)
continue;
char temp = words[i];
words[i] = words[j];
words[j]=temp;
j--;
}
int lastIndex = 0;
for (int i=0;i<words.length;i++){
if(words[i]==' '){
words = reverseWord(words,lastIndex,i-1);
lastIndex=i+1;
}
}
words = reverseWord(words,lastIndex,words.length-1);
return words;
}
private char[] reverseWord(char[] words, int from, int to) {
int j=to;
for(int i=from;i<((to/2)+from/2);i++){
if(i==j) continue;
char temp = words[j];
words[j]=words[i];
words[i]=temp;
j--;
}
return words;
}