switch strings without using any array notation in C - c

Hi I am kind of new to pointers and I am trying write a function that switches one string with another without using any array notation, so completely with pointers.
I have got this function, which works, but in this one, I use one array notation.
Is there an easier, better way to do this? Thank you.
void stringtausch(char *eins, char *zwei) {
char first[50], *philfe = first, *one = eins, *two = zwei;
while(*one != '\0') {
*philfe++ = *one++;
}
*philfe = '\0';
while(*two != '\0') {
*eins++ = *two++;
}
*eins = '\0';
philfe = first;
while(*philfe != '\0') {
*zwei++ = *philfe++;
}
*zwei = '\0';
}

If both strings have equal length
void stringtausch(char *eins, char *zwei) {
while ((*eins != '\0') && (*zwei != '\0')) {
char swp;
swp = *zwei;
*zwei++ = *eins;
*eins++ = swp;
}
*zwei = '\0';
*eins = '\0';
}
if they don't it's not possible unless you allocate enough space before passing the pointers. And I don't see much benefit in that since you can
void stringtausch(char **eins, char **zwei) {
char *swp;
swp = *zwei;
*zwei = *eins;
*eins = swp;
}
and call stringtausch
stringtausch(&s1, &s2);

Well an easier way is to use strcpy, but if you want to do your way, it's much easier. A pointer points to the first cell address of an array or single cell. You can merely just do this:
char * eins = "dogslovecats";
char * zwei = "cats";
stringtausch(&eins, &zwei);
void stringtausch(char ** eins, char ** zwei) {
char * temp;
temp = *eins;
*eins = *zwei;
*zwei = *temp;
}

Related

Copy array word-by-word vs letter-by-letter

I am currently using the following way to copy an array of strings:
void copy_sentinel_array(char ** buffer, char ** list) {
while (*list) {
*buffer = malloc(sizeof(char) * strlen(*list) + 1);
*buffer++ = *list++;
}
*buffer = NULL;
return;
}
It's simple enough and works fine. However, I'm having more trouble doing the same thing copying the words over letter by letter (so I can, for example, upper or lowercase the letters). For example:
void copy_sentinel_array(char ** buffer, char ** list) {
while (*list) {
*buffer = malloc(sizeof(char) * strlen(*list) + );
for (int i=0; i<=strlen(*list); i++)
(*buffer)[i] = tolower((*list)[i]); // let's lowercase it
buffer++;
list++;
}
*buffer = NULL;
return;
}
What would be the proper way to do this copy per-word and then per-letter?
The problem is operator precedence. *buffer[i] is equivalent to *(buffer[i]), but what you want is (*buffer)[i] (and the same for list).
If you don't need to process each character you can just use
strcpy(*buffer, *list);

Cannot retrieve all the data from Hashtable

I am trying to code an algorithm in C that reads a huge file ( more than 750.000 lines) , separates each line by some specified delimiters , and saves the data into a structure , which is then saved into a Hashtable. Everything goes fine until I want to print one specific data for each row of the Hashtable: the output is good for some rows , but the console is just printing some random symbols for others (which means memory leaks maybe? ).
I am trying to figure what can cause this problem. If I isolate the code that separate the line and saves it into the structure, and execute it for each line separately, it works fine, everything is printed as expected.
I have also tried to do it without dynamic allocation but it works a bit better with it as I was getting the infamous "Segmentation fault"
Here is the code that splits the line and saves it:
unsigned int hash(unsigned int id) {
unsigned int hashage = 5381; //Valeur arbitraire
unsigned int mdop = 10; //faire un modulo obtenir l'unite
int idtmp = id;
while (mdop < id) {
idtmp = id%mdop;
hashage = ((hashage << 6) + hashage) + idtmp;
mdop *= 10;
}
return hashage % NB_CASES_HASH;
}
void initiate_hashtable(Hashtable hashtable) {
int i = 0;
for (; i < NB_CASES_HASH; i++) {
hashtable[i] = NULL;
}
}
void ajout_entete(Liste *liste, Oeuvre *oeuvre) {
Liste p = malloc(sizeof(Cellule));
if (!p) exit(EXIT_FAILURE);
p->oeuvre = *oeuvre;
p->suiv = *liste;
//Si on imprime ici , tout va bien , les données sont correctes
*liste = p;
}
void ajout_annee(Liste *liste, Oeuvre *oeuvre) { //ajout trié par année pour recherche plus rapide
if (!(*liste) || oeuvre->year <= (*liste)->oeuvre.year)
ajout_entete(liste,oeuvre);
else {
if (oeuvre->year >= (*liste)->oeuvre.year)
ajout_annee(&(*liste)->suiv, &oeuvre);
}
}
Oeuvre peuple_oeuvre(char line[MAX_CHARS_LINE]) {
int i = 0, j = 1, cmpt = 0;
char strings[CHAMPS_OEUVRE][MAX_SIZE];
char carac = *(line);
char mot[MAX_SIZE];
mot[0] = carac;
bool isSuivi = false;
Oeuvre oeuvre;
while (carac != '\n') {
if (carac == ',') {
if(isSuivi) {
mot[j - 1] = '\"';
mot[j] = '\0';
isSuivi = false;
} else
mot[j - 1] = '\0';
strcpy(strings[i], mot);
j = 0;
i++;
} else
if (carac == '\"') {
cmpt++;
carac = *(line + cmpt);
while (carac != '\"') {
mot[j] = carac;
j++;
cmpt++;
carac = *(line + cmpt);
}
isSuivi = true;
}
cmpt++;
carac = *(line + cmpt);
mot[j] = carac;
j++;
}
mot[j] = '\0';
strcpy(strings[i], mot);
//Assignation des valeurs :
oeuvre.id = atoi(strings[0]);
oeuvre.accession_number = strdup(strings[1]);
oeuvre.artiste.nomArtiste = strdup(strings[2]);
oeuvre.artiste.artistRole = strdup(strings[3]);
oeuvre.artiste.artistId = atoi(strings[4]);
oeuvre.titre = strdup(strings[5]);
oeuvre.url = strdup(strings[CHAMPS_OEUVRE]);
oeuvre.year = atoi(strings[9]);
return oeuvre;
}
void peuple_hashtable(Hashtable hashtable) { // Peuplement par redirection
char ligne[MAX_CHARS_LINE];
fgets(ligne, MAX_CHARS_LINE, stdin);
Oeuvre *oeuvre = malloc(sizeof(Oeuvre));
int hashNum;
while (fgets(ligne, MAX_CHARS_LINE, stdin)) {
*oeuvre = peuple_oeuvre(ligne);
hashNum = hash(oeuvre->artiste.artistId);
ajout_annee(&hashtable[hashNum], oeuvre);
}
}
int main() {
Hashtable hashtable;
initiate_hashtable(hashtable);
peuple_hashtable(hashtable);
return 0;
}
And the Oeuvre structure looks like this :
typedef struct oeuvre {
unsigned int id;
char *accession_number;
Artiste artiste;
char *titre;
int year;
char *url;
} Oeuvre;
typedef Liste Hashtable[NB_CASES_HASH];
Thanks in advance.
There are many problems in your code.
If line does not contain a newline or if a double quote is missing, the behavior is undefined.
You do not initialize the string array: if the description has missing fields, the behavior is undefined.
In the part where you save the structure fields, your allocation code is incorrect: you must allocate one more character than the length of the string, strlen(string[0]) + 1 instead of strlen(string[0]) * sizeof(char*).
It would be much simpler to use the POSIX function strdup():
// Assigning the values:
oeuvre.id = atoi(strings[0]);
oeuvre.accession_number = strdup(strings[1]);
oeuvre.artiste.nomArtiste = strdup(strings[2]);
oeuvre.artiste.artistRole = strdup(strings[3]);
oeuvre.artiste.artistId = atoi(strings[4]);
oeuvre.titre = strdup(strings[5]);
oeuvre.url = strdup(strings[CHAMPS_OEUVRE]));
oeuvre.year = atoi(strings[9]);
Solved my issue by declaring the Oeuvre structure like this
typedef struct oeuvre {
unsigned int id;
char accession_number[MAX_CHARS];
Artiste artiste;
char titre[MAX_CHARS];
int year;
char url[MAX_CHARS];
} Oeuvre;
with MAX_CHARS referring to a large number.
So I believe I was not properly allocating the strings before using them, which made them to point to random adresses , resulting in those weird outputs but no error. I also believe that another way of solving this would be to dynamically allocate the chars for each Oeuvre in my function.

Iterate through a char pointer from a struct

So I'm trying to combine two strings together but I'm getting str is a read only value on the last line of the second while loop. Is their anyone can I do this without changing the function header?
Also String is a struct I created that has a *char called str.
String * String_Combine(String * tar, const String * src) {
//end of string
while (* tar->str != '\0') {
tar->str++;
}
//int i = 0;
//copy string
while (*source->str != '\0') {
*tar->str = *src->str;
*tar->str++;
*src->str++;
// i++;
}
return tar;
}
Copy the pointer before modifying it. I guess modifying tar->str may also be harmful because it will destroy the information that where the string starts.
String * String_Combine(String * tar, const String * src) {
char * tar_str = tar->str, * src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy string
while (*src_str != '\0') { /* assuming that "source" is a typo and it should be "src" */
*tar_str = *src_str; /* with hope that there is enough writable buffer allocated */
tar_str++;
src_str++;
// i++;
}
//terminate string
*tar_str = '\0';
return tar;
}
Two things:
Make sure you allocate enough memory for tar->strto hold both tar->str and src->str
Store a pointer tar/src->str locally and iterate thought them so you wouldn't loose the pointer to original str;
I wrote a test case for you to understand it easily ;)
#include <iostream>
#include <string.h>
struct String {
char* str;
};
using namespace std;
String * String_Combine(String * tar, const String * src) {
// take a local copy of strs
char* tar_str = tar->str;
char* src_str = src->str;
//end of string
while (* tar_str != '\0') {
tar_str++;
}
//int i = 0;
//copy src string to tar string
while (*src_str != '\0') {
*tar_str = *src_str;
*tar_str++;
*src_str++;
// i++;
}
return tar;
}
int main()
{
String* src = (String*) malloc(sizeof(String));
src->str = new char[20];
strcpy(src->str, "World!");
String* tar = (String*) malloc(sizeof(String));
tar->str = new char[20];
strcpy(tar->str, "Hello ");
String* result = String_Combine(tar,src);
cout << result->str << endl;
return 0;
}

C creating a String Stuct

So, I've been working on implementing this struct for a String. However, I keep on getting a Segmentation Fault when calling *createString();
Here us the .h contents
typedef struct {
char *characters;
int length;
} String;
String *createString();
Here is the implementation in my .c file
String *createString(){
char *m,b;
int n = 0;
String *theS = (String *) malloc (sizeof(String));
m = theS->characters;
b = getchar();
while((b = getchar()) != '\n'){
*(m+n) = b;
n++;
m = realloc(m, n+1);
}
*(m+n) = '\0';
theS->length = strlen(theS->characters);
return new;
}
Problem 1
Q: after this line:
String *theS = (String *) malloc (sizeof(String));
what does theS->characters point to?
A: Who knows? Nowhere useful, though.
You'll need to allocate at least a character, to hold the '\0' that eventually gets inserted.
String *theS = malloc (sizeof(String));
theS->characters = malloc(1);
Problem 2
You then modify m all over the place, but never reassign that value to theS->characters, so when you say
theS->length = strlen(theS->characters);
that's not going to be a very helpful answer.
Right before that line, add:
theS->characters = m;
Problem 3
return new;
should probably be:
return theS;
Problem 4
You're throwing away the first character. Just remove the standalone b = getchar(); line.
Working example:
https://ideone.com/tm1TG9
As #HuStmpHrr suggests: when you allocate your String, you don't allocate any space for its characters field to point to, so when you try to access what it points to, things will go bad.
Using your style and not checking allocation errors:
String *createString(){
int n = 0;
char *m = malloc(1);
int c;
String *theS = (String*)malloc(sizeof(String));
while ((c = getchar()) != EOF && c != '\n') {
m = realloc(m, n + 2);
m[n] = c;
n++;
}
m[n] = '\0';
theS->characters = m;
theS->length = n;
return theS;
}

using functions in c (return value)

Learning C and having many doubts.
I have a function (lets say function 1) that calls another function (lets say function 2).
Function 2 calculates an array of string.
How can I use this array in function 1?
Some code example:
int find_errors(char* word)
{
char error[100];
/*Given the word, It will find the duplicate chars and store it in the
error array. */
return 0;
}
int find_word(char* word)
{
find_errors (word);
printf("%s\n", error);
return 0;
}
There are at least three possible approaches:
Use a global variable
pass a parameter between them
return a pointer from the function
There are multiple ways to do this.
1) Create a dynamic array and return a pointer to the array. This will require you to manually free the memory for the array at a later time.
#define NUM_ELEMS 50
// In find_error():
char* error = malloc(NUM_ELEMS * sizeof(char));
return error;
// In find_word():
char *error = find_errors();
// do stuff
free(error);
2) Pass a pointer to find_errors that it can use as the error array. This will not require you to manually free the memory.
// In find_word():
char error[NUM_ELEMS];
find_error(error);
3) Use a global array. May make it more difficult for other people to understand your code. Has other potential problems as well.
// In global scope:
char error[NUM_ELEMS];
Your question relates to "call-by-reference" and "call-by-value".
char* getNewValsToSet(void)
{
char* new_vals = (char*) malloc(sizeof(char[5]));
new_vals[4] = '\0';
return new_vals;
}
void setValuesEven(char* vals_to_set)
{
vals_to_set[0] = 'A';
vals_to_set[2] = 'C';
}
void setValuesOdd(char* vals_to_set)
{
vals_to_set[1] = 'B';
vals_to_set[3] = 'D';
}
int main(void)
{
char* some_vals_to_set = getNewValsToSet();
setValsEven(some_vals_to_set);
setValsOdd(some_vals_to_set);
// ... now has vals "ABCD"
free(some_vals_to_set); //cleanup
return 0;
}
If you have "doubts" about learning C, IMHO it's one of the best things you can do (no matter the language in which you work) because it will explain exactly how things work "under-the-hood" (which all high-level languages try to hide to some degree).
You need to declare the error array globally and use it just like you did.
EDIT: using global variables isn't the best practice in most of the cases, like this one.
Here is an example of what you are looking for with an awesome console output. It dynamically allocates the array to hold any number errors (duplicate characters in your case) that may occur.
//Only free errors if result is > 0
int find_errors(char* word, char** errors)
{
int num_errors = 0;
int word_length = strlen(word);
int ARRAY_SIZE = MIN(8, word_length);
char existing[word_length];
int existing_index = 0;
*errors = NULL;
for(int i = 0; i < word_length; i++)
{
char character = word[i];
//Search array
for (int n = 0; n < word_length; ++n ) {
if(n >= existing_index)
{
existing[n] = character;
existing_index++;
break;
}
if (existing[n] == character) {
num_errors++;
if(!*errors)
*errors = (char*)malloc(ARRAY_SIZE * sizeof(char));
//Check if we need to resize array
if(num_errors >= ARRAY_SIZE)
{
ARRAY_SIZE *= 2;
ARRAY_SIZE = MIN(ARRAY_SIZE, word_length);
char *tmp = (char*)malloc(ARRAY_SIZE * sizeof(char));
memcpy(tmp, *errors, (unsigned long)ARRAY_SIZE);
free(*errors);
*errors = tmp;
}
//Set the error character
(*errors)[num_errors - 1] = character;
break;
}
}
}
return num_errors;
}
int find_word(char* word)
{
char* errors;
int errCount = find_errors (word, &errors);
if(errCount > 0)
{
printf("Invalid Characters: ");
for(int i =0; i < errCount; i++)
{
printf("%c ", errors[i]);
}
printf("\n");
free(errors);
}
return 0;
}
int main(int argc, char *argv[])
{
find_word("YWPEIT");
find_word("Hello World");
find_word("XxxxXXxXXoooooooOOOOOOOOOOOOOOOooooooooOOOOOOOOOOOOooooooOOO");
}

Resources